Custom Classes – Iterator + Container

January 16th, 2012

This article covers making a custom iterator type container in PHP. When I talk about a “container” I mean a class that holds something, often in a similar was that an array does. Adding iterator capabilities to it means it will work with a foreach statement like an array does too, and once you complete it you will then be able to add custom functionality to the class that goes beyond what an array can do. (You will need PHP 5+ to use this code.)

I’ll just show you the whole class, and you can take a look and try to grasp what is going on.

class container implements Iterator{

    private $var = array();

    public function __construct()
    {
    }
    public function set($value, $key=-1)
    {
        if($key == -1){
            $this->var[] = $value;
        }else{
            $this->var[$key] = $value;
        }
    }
    public function delete($key)
    {
            unset($this->var[$key]);
    }
    public function get($key)
    {
        return $this->var[$key];
    }
    public function count()
    {
        return count($this->var);
    }
    public function rewind()
    {
        reset($this->var);
    }
    public function current()
    {
        $var = current($this->var);
        return $var;
    }

    public function key()
    {
        $var = key($this->var);
        return $var;
    }

    public function next()
    {
        $var = next($this->var);
        return $var;
    }

    public function valid()
    {
        $key = key($this->var);
        $var = ($key !== NULL && $key !== FALSE);
        return $var;
    }

}

The class is just based on the standard example from the PHP manual. It is different however, it allows you to set and get (and unset) variables in the array that is inside the class. In a way this is a wrapper for an array, rather than inheriting from an array or fully duplicating an array’s capabilities. The class does inherit from the Iterator class.

The functions rewind(), current(), key(), next(), and valid() are what make the iterator capability work. We don’t really have to have them defined this way if we inherit from the right class, but I wanted to show them defined because it would allow you to create custom behaviors there in the future. That’s the point of this class. As it is it is just an inferior version of an array, but once you start customizing it the possibilities are endless.

Next we just test it and get familiar with using it.

$mycontainer = new container();
$mycontainer->set("Cats", 9);
$mycontainer->set("DOGS", 90);
$mycontainer->set("Rats", 900);
foreach($mycontainer as $key => $value){
    echo "$key :: $value n";
}

Exactly as you probably thought it would work, 3 halues are set and 3 are printed. The second parameter of set() is the key, which can be omitted. The next example does not use the iterator part of the class but note that it basically doesn’t work if you use the second parameter of set. It is only functional in the following manner.

$mycontainer = new container();
$mycontainer->set("Cats"); //with second parameter omitted this is key 0
$mycontainer->set("DOGS");//1
$mycontainer->set("Rats");//2
for($i = 0; $i < $mycontainer->count(); $i++){
        echo "$i :: " .  $mycontainer->get($i) . " n";
}

That should have printed the same values as before but with the keys 0, 1, 2. Now you can see why the iterator is usefull for anything that does not use completely continuous numeric keys. Even using $mycontainer->delete(1); would throw off the for() loop.

Now that you have a class and know how to use it, try to think of ways to make it useful. There are many possible features that a container class could have:
Sorting
Searching
Hashing
Type Checking
Error Checking
Checking for duplicates
Limited Sizes
Variable iterator start and stop indexes
Variable type returns like choosing to iterate as float or int
Efficiency in speed or by using less memory

Constants, and why they are used

January 13th, 2012

In programming, a constant is anything that is supposed to be the same all the time and cannot be changed. Basically is is that way at least until the program starts again. In PHP constants are not similar to variables in syntax, which is in some ways a surprise. I’ll quickly show you a C constant and then a PHP constant.

constants in C:
const int AARDVARK = 2;

constants in PHP:
define(‘AARDVARK’, 2);

normal variables in PHP:
$AARDVARK = 2;

So the first thing to keep in mind is that constants are defined and used a bit differently than variables in PHP.

define('MAXIMUM', 500);
if($amount > MAXIMUM){
    die("amount was greater than maximum!");
}

That example pretty much sums up normal constant usage. If somehow we didn’t know if the constant was set yet we could do something like this.

if(defined('MAXIMUM') == false)
    define('MAXIMUM', 9999);
if($amount > MAXIMUM){
    die("amount was greater than maximum!");
}

Back to the first concept, why choose to make something a constant rather than a variable. It’s often intended to be a technique to minimize bugs, even though the bugs would be caused by the programmers. It’s just safer to have a value that cannot be edited rather than one that can but will cause problems if it is. There are also reasons like clarity, separating regular variables visually from constant data. In PHP constants are also super globals, similar to $_POST for example, they can be used anywhere once defined. All the more reason that you cannot set a constant to a different value, since you have global access to it. Predefined Constants are another major part of PHP as they handle a lot of things specific to various areas of PHP without taking up variable names. For example the MySQL module in PHP has a lot of constants that are defined before your script runs.

Let’s try to answer some questions that may come up…

Why are constants always uppercase?
They are not, they can use the same characters that variables use. They are often that way because it is a convention in programming to make defined global values all uppercase so they are easily spotted, and other reasons.

Can I unset a constant?
As you know they cannot be changed, and in PHP they cannot be unset or deleted.

Can I use a string with the name of a constant to get a constant’s value?
Yes, the function constant() does that, $value = constant(‘CONSTANTNAME’);

Some constants have 2 underscores in front and 2 behind, what do they do?
Some are normal constants and it’s just a way to name them. Some are Magic Constants.

Magic constants?
You can find a list of them in the PHP manual. They are defined automatically, and often change by themselves depending on what file the script is in, what line it is on, or what function it is used in. They aren’t much like constants in that respect.

Going Retro With PHP – A Hits Counter Step By Step

January 11th, 2012

Going Retro With PHP – A Hits Counter Step By Step

It wasn’t all that long ago that the web was littered with little “number of visitor” indicators. The point of these was either to show how popular your site was, or more likely just because the site owner had no administrator level site statistics program running. Now most sites have a sophisticated statistics system or other means of counting visitors and these visible counters have become less popular. It will still be a good learning experience to try making a script for counting visitors from scratch.

A simple function to detect a valid page load might look like this

function log_page_load(){
    $invalid_list = '/(googlebot|crawler)/i'; //set a regular expression that detects invalid agents like googlebot
    if (preg_match($invalid_list, $_SERVER['HTTP_USER_AGENT'])){
    // it is a bot
    }else{
    // it is probably a browser
    }
}

As you can see we are erring on the side of caution, making browsers the default for most possible strings. That is because there are probably countless possible browsers now with cell phones and other devices included. There’s no easy way to tell bot from human computer user there may be no perfect method that is always right. There definitely are techniques that are pretty accurate compared to what I show above but they are beyond the scope of this article.

Once we have decided the request is a real pageview we can get on with the reading and saving of data.

$filename = "counterfile.dat";
if(file_exists ($counter_file)){
    $counter_file = fopen($filename, 'r+');
    $total_count = trim(fread($counter_file, filesize($filename)));
}else{
    $counter_file = fopen($filename, 'w');
    $total_count = 0;
}
//other code here
...
//save file
ftruncate($counter_file,0);
fwrite($counter_file, $total_count);
fclose($counter_file);

First we opened the file, but since it may not exist we check that, and create it if it doesn’t (opening with w will do that). Otherwise it should work fine to read it. To save it we erase it (ftruncate($counter_file,0)) and write new data, which is just a number. In the next example let’s combine all that we have covered.

function log_page_load(){
    $filename = "counterfile.dat";
    if(file_exists ($counter_file)){
        $counter_file = fopen($filename, 'r+');
        $total_count = trim(fread($counter_file, filesize($filename)));
    }else{
        $counter_file = fopen($filename, 'w');
        $total_count = 0;
    }
    $invalid_list = '/(googlebot|crawler)/i'; //set a regular expression that detects invalid agents like googlebot
    if (!preg_match($invalid_list, $_SERVER['HTTP_USER_AGENT'])){
        //it is a brower. probably. increment and save.
        $total_count++;
        ftruncate($counter_file,0);
        fwrite($counter_file, $total_count);
    }
    fclose($counter_file);
    if(!trim($total_count))
        $total_count = 0; //let's make sure it is a 0 if it somehow was set to a blank string
    return $total_count;
}
echo "val= ". log_page_load(); //each load you get 1, 2, 3, 4

so there you have it, define the function and either capture the return value for later or print it as shown.
Isn’t there something missing though? Most of the counters are graphical since they are used on sites that have no scripting. How can we get graphics instead of just numbers as a text string? There are many ways, but try this on for size.

$page_loads = log_page_load(); //logs once and also return the new value
$page_l_str = strval($page_loads);
$icons = "";
for($i = 0; $i < strval($page_l_str); $i++){
    $icons .= '<imr src="images/' . $page_l_str[$i] . '.gif" />';
}
echo $icons;

that will produce image tags with the pattern <imr src=”images/1.gif” /> and so on. $page_l_str[$i] means get the letter with index $i.
Of course you might also wish to zero padd it or instead of images use <li>$page_l_str[$i]</li>, there are a limitless variety of possibilities with modern css and php.

To take this a step further you might want to build in counting of visitors instead on top of pageviews. That would be done by recording $_SERVER[‘REMOTE_ADDR’] and checking if the visitor has been there recently. You could use 2 counters for hits and unique hits, or some other combination. That’s all I will cover here though so that concludes the article on hit counters, I hope it has been informative.

Custom Math High and Low Functions in PHP

January 11th, 2012

This should be a good beginner tutorial, especially if you have used some functions and if statements and haven’t gone much further. I will explain the concept as we go along, first let’s review the Ternary Operator / Operators that we will use.

$x = $y < 0 ? -1 : 1;

That is sort of a quick “what is the sign” test for signed numbers. If means if y is less than 0 x is negative 1, otherwise it is positive 1. 0 is considered +1 in that case. Following so far? You just need to understand the basic rules so you can understand what is happening in a complex line of code.

$x = $y == 0 ? 0 : ($y < 0 ? -1 : 1);

In that case it mean if y is 0 x is 0, otherwise x is either -1 or +1. Let’s use that principal to make a function that accepts 3 arguments and tells us whether argument 1 is within the bounds of arguments 2 and 3.

function custom_clamp_check($number, $low, $high){
    return $number > $high ? false : ($number < $low ? false : true);
}

Think about that function like this, if the first statment is true, we return false, if it isn’t we check the second statment. The function returns false unless the $number is between $high and $low or equal to either. This is intentionally written to work well with floats and integers. Other variable types should not be used.

PHP has built in min and max functions like in this example.

$x = min($y, 0);
$x = max(5, 12);

That just gives you the higher number of the two numbers put into the function. Actually you can do max(1, 4, 99, -5) since it allows more arguments, or max(array(1, 4, 99, -5)) with the same result. It returns the highest of whatever is put in. I don’t know if min/max have many pros or cons over statements like we used earlier, but some users have said min/max are slower and less efficient. Regardless of that this article is focusing on using operators in statements to construct the desired functions, even if only for learning purposes.

Our final function is a clamp function, it clamps the value entered to be equal to $high, equal to $low, or between the two.

function custom_clamp($number, $low, $high){
    return $number > $high ? $high : ($number < $low ? $low : $number);
}

You should now understand how the function works and what the results will be. custom_clamp(1.0115, 1.0, 0.0) would give you 1.0, the exact high value you put in. These functions are useful for many things, and as I described earlier we can use integers or floats. If a user entered a comment we could check if it’s length is within the range we want.

if(custom_clamp_check(strlen($_GET['comment']), 20, 512) == false){
    return "Your comment must be 20 to 512 chars in length.";
}

If the user must also enter a number within 1 and 10 and we want to make sure there are no other numbers possible, we could force it with the clamp function.

$entered_number = custom_clamp(intval($_GET['number']), 1, 10);

I added intval to make this even more strict, since users could have entered 1.0002 and we probably only want whole numbers. The final use shows how useful the function can be in math. The goal is to keep the product above 0 since dividing by zero is not acceptable.

$angle = 1.0 / custom_clamp($radius * pi(), 0.0000001, 1.0);

PHP – What in the world is CType

January 7th, 2012

Apparently the name comes from ctype.h, a file in the C programming language. The PHP engine can execute the code from that file in it’s ctype functions. Does the c in ctype stand for classification, or character maybe? I don’t know, but according to PHP, these functions “are always preferred over regular expressions, and even to some equivalent str_* and is_* functions.” because they are executed from the executable functions (from ctype.h) and are therefore faster.

$is_it_alphanumeric = ctype_alnum("Carnival101"); //true
$is_it_alphanumeric_too = ctype_alnum("Carnival*_*");//false
$is_it_alphanumeric_also = ctype_alnum("Carnival1.01"); //false

ctype_alnum() checks if the string is totally alphanumeric. The first of those was the only one, a decimal is still not going to return true. Checking if things are alphanumeric is actually pretty useful, but it’s probably more common to check if it is an unsigned integer for example.

$is_it_digit = ctype_digit("05909090"); //true
$is_it_digit_too = ctype_digit(-4); //false
$is_it_digit_also = ctype_digit(4.5); //false

So only 0s to 9s will get you a true result. Positive integers will also work. ctype_alpha() is the same as those 2 but for letters only. Going back to ctype_alnum, it also accepts integers but “If an integer between -128 and 255 inclusive is provided, it is interpreted as the ASCII value of a single character ” So if you aren’t interested in ASCII tests don’t pass integers to ctype_alnum(). That rule applies to the next function as well, called ctype_print();

ctype_print("asdfnrt"); // false

ctype_print() only returns true if the characters are printable and not control characters. This is a confusing classification, since spaces would be allowed and tabs not. Only use it if you are looking for a very specific type of check. ctype_graph() might be more clear, as it is the same but spaces are not valid, no white space is allowed. ctype_cntrl() is the opposite of ctype_print(), the characters must be control characters to get it to return true.

ctype_lower() returns true for lowercase letters only
ctype_punct() returns true for punctuation only
ctype_space() returns true for white space only (tab, space, newline and so on)
ctype_xdigit() returns true for hexidecimal characters only (comes in useful for checking if the string is a color code for example)
ctype_upper returns true for uppercase letters only

For all these functions, empty strings will give you false in new versions of PHP, but true in older versions (< 5.1). Passing something other than an integer or string will return false.

Let Your Users Log In

January 6th, 2012

Something everyone should do once ( or several times ) when learning PHP is create a simple user and log in system. It is the basis of many different types of web sites, forums, WordPress sites, ebay, even search engines (Google) let you log in to customize your experience and access advanced features.

Let’s stick to just the user system here, no fancy features or tricky security. Just the basics that you can expand on later. The steps will be Registration, Log In,  Log Out. To save space I won’t include the html that is involved, so you’ll need to take some initiative and handle that part.

CREATE TABLE `site_users` (
  `name` varchar(255) NOT NULL,
  `email` text NOT NULL,
  `password` text NOT NULL,
  `status` varchar(255) NOT NULL default ''
);

There’s your user table in sql query format. We will be using MySQL and it is probably easiest to just add that table in a PHPMyAdmin interface unless you have a better way already.

Next create a page that shows some input fields for registration. name email and password are fine.

<?php
//first connect to MySQL and select the database you want, use your own code here
$conn = mysql_connect($database_host, $database_user, $database_pass);
mysql_select_db($database_name);
//initializing variables
$completed_message = '';
$error_message = '';
//then check if there was a post, there are many ways to check, use whatever you prefer
if($_SERVER['REQUEST_METHOD'] == 'POST'){
    if(strlen($_POST['name']) < 3){
        $error_message = "the 'name' was not long enough";
    }elseif(strlen($_POST['email']) < 3){
        $error_message = "the 'email' was not long enough";
    }elseif(strlen($_POST['password']) < 3){
        $error_message = "the 'password' was not long enough";
    }else{
    //insert data:
        mysql_query("INSERT INTO site_users SET name='".$_POST['name']."', email='".$_POST['email']."', password='".$_POST['password']."'");
        $completed_message = "Your new account has been created, sign in now";
    }
}
//if $completed_message != '' then we did not submit the form, or it failed. if it was blank for example it would fail
if($completed_message != ''){
    echo $error_message;
//show an html form  for registration that includes a field for name, password, email, and a submit button
...
}else{
    echo $completed_message; //and that's it, the form was submitted and completed
}
?>

Did you have trouble reading through that example file? If so review MySQL queries and other basics and it should become clear. I used the format “INSERT INTO site_users SET x=y etc.” which isn’t as common as other insert statement formats, but I find it to be superior. Since we checked all the fields for length by using strlen(…) < 3 we are sure that there was at least something entered into the fields. It’s a terribly incomplete check but you can probably figure out how to add more tests depending on your needs. You should always make sure strings are escaped, don’t allow unescapped user input in MySQL queries.

If for example you want to make sure they don’t just fill fields with spaces you can use regular expressions to see what kind of characters were entered.

There is one test I specifically want to show you. Assume the same file as above, but instead of just the insert query:

...
}else{
    //insert data:
        $test_name = mysql_query("SELECT name FROM site_users WHERE name = '".$_POST['name']."'");
        if(mysql_num_rows($test_name) > 0){
            $error_message = "That name is already in use, please enter a new name.";
        }else{
            mysql_query("INSERT INTO site_users SET name='".$_POST['name']."', email='".$_POST['email']."', password='".$_POST['password']."'");
            $completed_message = "Your new account has been created, sign in now";
        }
    }
...

That is one of several ways to prevent a user from using a name that already exists. The name needs to be unique. You could also repeat that for email addresses if you want users to only have one account for 1 email address. Start a new file for the next example.

<?php
//first connect to MySQL and select the database you want, use your own code here
...
//start a session
session_start();
//initializing variables
$completed_message = '';
$error_message = '';
$session_message = '';
//then check if there was a post, there are many ways to check, use whatever you prefer
if($_SERVER['REQUEST_METHOD'] == 'POST'){
    if(strlen($_POST['name']) >= 3){
        $get_user = mysql_query("SELECT * FROM site_users WHERE name = '".$_POST['name']."'");
        $user_row = mysql_fetch_array($get_user);
        if(!$user_row){
            $error_message = "there is no user by that name";
        }elseif($user_row['password'] != $_POST['password']){
            $error_message = "the entered password was incorrect";
        }elseif($user_row['status'] == 'banned'){ //I'll explain this later
            $error_message = "you have been banned from this site, you cannot log in";
        }else{
            $completed_message = 'you are now logged in, welcome!'
            $_SESSION['site_user']['name'] = $user_row['name'];
            $_SESSION['site_user']['password'] = $user_row['password'];
        }
    }else{
        $error_message = "enter your name and password";
    }
}else{
    if( $_SESSION['site_user']['name'] ){
        $get_user = mysql_query("SELECT * FROM site_users WHERE name = '".$_POST['name']."'");
        $user_row = mysql_fetch_array($get_user);
        if(!$user_row){
            $error_message = "your session is invalid";
        }elseif($user_row['password'] != $_SESSION['site_user']['password']){
            $error_message = "your session is invalid";
        }elseif($user_row['status'] == 'banned'){
            $error_message = "you have been banned from this site, your session is invalid";
        }else{
            $session_message = "Logged in as ".$user_row['name'];
        }
        if($session_message == ''){
            unset($_SESSION['site_user']);
        }
    }
}
//if $completed_message != '' then we did not submit the form, or it failed. if it was blank for example it would fail
if($completed_message != ''){
    echo $error_message;
    echo $session_message;
//show an html login form that includes a field for name, password, and a submit button
...
}else{
    echo $completed_message;
}
?>

That probably seemed unnecessarily long but I wanted to show some details of what goes on in a login system. It checks carefully that you are properly logged in on every page load. Sessions are a way of handling a user or login behind he scenes using cookies and server based storage. session_start() is always called early to ensure that it is before any headers or html output. Make sure any html is below the main PHP blocks in these files for that reason.

Once you have logged in you stay logged in for a while, or until the browser closes. These settings can be adjusted with PHP’s session functions.

To logout, just make a link to a logout page.

<?php
//first connect to MySQL and select the database you want, use your own code here
...
//start a session
session_start();
unset($_SESSION['site_user']);
echo "you are now logged out";
?>

The field called ‘status’ is in the original table and is checked in the login portions. Status is blank normally, meaning the user is valid and ‘normal’. If a user did something wrong and you want to keep them off the site, well that is what the status ‘banned’ is for. It is checked in the login area and the user is never logged in if they are banned. They are also logged out the moment the system is able to once they are banned.

The code to ban someone would go something like this:

<?php
//first connect to MySQL and select the database you want, use your own code here
...
mysql_query("UPDATE site_users SET status='banned' WHERE name = '".$_POST['name']."'");
...
?>

Unlimited Arguments

January 6th, 2012

If you’ve used printf/sprintf/etc. in PHP you may be wondering how these functions can accept any number of arguments and still make sense of it. You also may wonder how you can make functions that employ this ‘trick’. Well it’s not a trick really, it seems to be built in to every function the user defines.

function play(){
	$args = func_get_args();
    foreach ($args as $k => $v) {
        echo "act ".($k+1).": $v <br />n";
    }
}
play("intro", "the hunt", "the wedding", "the tragedy", "the finale", "the end");

It prints the arguments even though they are not defined. Is it only with functions with no defined arguments? No. What happens when we define arguments as well.

function play($act_1, $act_2){
	$args = func_get_args();
	echo "Act 1 will be $act_1 ... <br /><br />n";
    foreach ($args as $k => $v) {
        echo "act ".($k+1).": $v <br />n";
    }
}
play("intro", "the hunt", "the wedding", "the tragedy", "the finale", "the end");

It will just print the same as before for each act. That means defining arguments has virtually no effect on the function func_get_args().

Ever tried giving say, substr() 6 arguments though…

Warning: substr() expects at most 3 parameters, 6 given in C:propEasyPHPwwwbwordsmiscunlimited_arguments.php on line 16

That means that built in functions thrrow errors in that situation, while user created ones don’t. It’s not a critical component of PHP programming but it helps to know what will happen

function play($act_1, $act_2){
	echo func_num_args();  //echoes 6
}
play("intro", "the hunt", "the wedding", "the tragedy", "the finale", "the end");

If you try the above code you will see that func_num_args() return the number of arguments the user entered not the number of defined arguments, which is intentional. func_get_arg($arg_num) simply returns the function with the index entered. The first is 0 for example. That’s the important stuff when dealing with argument lists.

In PHP gzcompress is easy, and often useful

January 6th, 2012

xœKLJNIMK Û½

That is what I got when I ran the following code:

echo gzcompress("abcdefg");

In the title I said “often useful”. If you look closely you’ll see why. The compressed version takes more bytes than the uncompressed. It’s not a surprise of course, the feature was created for compressing a file or something of considerable size, at least with a bigger size than 7 bytes (though characters may or may not take up a byte each I suppose…).

echo strlen(gzcompress("A camel is an even-toed ungulate within the genus Camelus, bearing distinctive fatty deposits known as humps on its back. There are two species of camels: the dromedary or Arabian camel has a single hump, and the bactrian has two humps. Dromedaries are native to the dry desert areas of West Asia, and Bactrian camels are native to Central and East Asia. Both species are domesticated; they provide milk and meat, and are working animals. "));

There, we printed the compressed versions size. I measured 258 chars for compressed and 2439 for the uncompressed version. That’s not even a very large chunk of text, so clearly gzcompress() will give a good reduction in almost all situations. As for how to uncompress:

$gz_string = gzcompress($reg_string);
echo strlen($gz_string);
$un_reg_string = gzuncompress($gz_string);
echo strlen($un_reg_string);

So to sum it up you won’t have any problems as long as you use strings not arrays for example. Compressing and uncompressing them in very useful and it’s great the PHP makes it so easy. Be sure to check out PHP’s other compression capabilities too.