Archive for PHP

PHP writeUTF implementation

We currently have an application in Groovy/Grails/Java that we’re slowly porting to PHP (for more affordable hosting costs).

We encountered a problem when we needed to convert one of our routines for writing out a binary file. We were using Java’s DataOutputStream.writeUTF(String) method and had a hard time trying to write binary data in PHP.

After much research on the web, we found a method in PHP called pack(). Below is our PHP implementation of Java’s DOS.writeUTF(String).

	public static function writeUTF($string) {
		$utfString = utf8_encode($string);
		$length = strlen($utfString);
		print(pack("n", $length));
		print($utfString);
		flush();
	}

We don’t claim this to be the exact equivalent of the method, but it gets the job done and the receiving end of the file was able to parse the binary file properly with the new PHP implementation with no modifications to the client code.

Please checkout the pack() documentation for more details.

Comments (1)

Zend Framework Large File Upload Issues

I tried modifying this Zend_Form_Element_File example to support larger file uploads but noticed that I’ve been getting “Undefined index” errors when my uploads are bigger than 8MB.

Notice: Undefined index: tmp_name in C:\quickstart\library\Zend\File\Transfer\Adapter\Abstract.php on line 589

Notice: Undefined index: name in C:\quickstart\library\Zend\Validate\File\Size.php on line 398

Notice: Undefined index: tmp_name in C:\quickstart\library\Zend\File\Transfer\Adapter\Abstract.php on line 589

Notice: Undefined index: name in C:\quickstart\library\Zend\Validate\File\Extension.php on line 228

Being a PHP and Zend Framework newbie, I wanted to put the blame on Zend but in the end I found the reason for the upload and validation errors after much googling.

The solution requires updating two properties in php.ini:

upload_max_filesize = 32M
post_max_size = 32M

With these changes you can now upload relatively bigger files!

Just make sure that your hosting provider supports your file upload size settings.

Comments (4)

WordPress Thank a Plugin Developer Day

Yesterday, I tried working on my first ever WordPress plugin. It’s a simple filter which includes a Google Adsense script after every post.

I’m thinking of creating a Buy me a Coffee plugin next to take this plugin development challenge on to the next level.

Coincidentally, Matt just announced January 28 as the official WordPress Thank a Plugin Developer Day.

Hopefully next year my plugins would be included in the next 4000 plugins for WordPress.

But for now, I’d like to thank the developers of Google Sitemap Generator and Syntax Highlighter Plus for a job well done.

Keep up the good work you guys!

Leave a Comment

Some "Really Neat" PHP Resources

I’m currently working on a personal PHP project and I just wanted to sort of “bookmark” the resources I’ve gathered and found useful over the past few days.

This is just my second PHP project and I feel like I’m beginning to like PHP better and better by the day.

Hope you also find these resources “really neat”.

Comments (3)

PHP 101: A Simple Seat Reservation System

I would like to share a little application I developed last year for a simple concert that our church sponsored.

The requirement was to develop a seat/ticket reservation system which will be accessed from two different locations.

The application will be used by administrators only to check the available seats and will not be accessible to end users.

Database Design:


CREATE TABLE seats (
   rowId varchar(1) not null,
   columnId int not null,
   status int,
   updatedby varchar(10),
   PRIMARY KEY (rowId,columnId)
);

CREATE TABLE userauth (
   rowID TINYINT UNSIGNED NOT NULL AUTO_INCREMENT,
   commonname VARCHAR(35) NOT NULL,
   username VARCHAR(8) NOT NULL,
   pswd VARCHAR(32) NOT NULL,
   PRIMARY KEY(rowID)
);

I tried to make the tables as simple as possible.

For the seats table, I used the rowId and columnId columns as the PK. The status column represent the state of a particular seat (0-available, 1-reserved, 2-confirmed). The updatedby column is for storing the name of the user who last updated the record (importance of this column will be discussed later).

I also needed some form of authentication which is contained in the userauth table. I think the columns for this table are self-explanatory, so there’s really no need to go into more detail about that.

I created a login.php page for authentication. I had to embed this page to all the other pages which I wanted to protect from unauthorized access.


function authenticate_user() {
   header('WWW-Authenticate: Basic realm="Tickets"');
   header("HTTP/1.0 401 Unauthorized");
   exit;
}

If you try to access a page which invokes the fragment above then you will be presented with a screen similar to what is shown below:

A word of caution from the Beginning PHP and MySQL 5 book:

Note that unless output buffering is enabled, these commands must be executed before
any output is returned. Neglecting this rule will result in a server error, because of the violation
of the HTTP specification.

You can retrieve the values entered into this form through the following scripts:


$_SERVER[PHP_AUTH_USER] // for retrieving username
$_SERVER[PHP_AUTH_PW] // for retrieving password

The values retrieved from the form are then compared to the entries in the userauth table. And if no match is found, the login form is re-displayed to the user.


// Connect to the MySQL database
mysql_pconnect("localhost", "tickets", "tickets")
   or die("Can't connect to database server!");
mysql_select_db("tickets") or die("Can't select database!");

// Check for matching users
$query = "SELECT username, pswd FROM userauth"
   . " WHERE username='$_SERVER[PHP_AUTH_USER]'"
   . " AND pswd='$_SERVER[PHP_AUTH_PW]'";
$result = mysql_query($query);

// Re-display login form
if (mysql_num_rows($result) == 0) {
authenticate_user();
}

I do hope all these snippets still make sense to you because we are just about to proceed to the more complicated stuff.

Given the seat layout of the concert hall, I decided to represent the rows using letters and the columns as numbers. The tricky part was how to dynamically generate the seat layout based on the rows retrieved from the seats DB table.

The next fragments were taken from seats.php


// Select all the seats in the venue
// The seat numbers in the concert hall were read from right to left
// (which is why columnId is sorted as desc)
$query = "SELECT * from seats order by rowId, columnId desc";
$result = mysql_query($query);

// Iterate through results, assign values to rowId, columnId,
// status and updatedBy variables
while (list($rowId, $columnId, $status, $updatedby)
   = mysql_fetch_row($result))

We now begin with the fun part, for every seat, which is still available, create a checkbox. For every seat which is reserved, also create a checkbox only if the user currently logged in is also the same user who reserved the seat. You may be wondering, why do I need to create a checkbox for a seat that’s already reserved? This is because, in this application, the reserved state is not the final state for a seat. A reserved seat can still be canceled or confirmed. But once a reserved seat is confirmed, no further modifications can be made to that seat.

echo "\n
<td bgcolor='$seatColor' align='center'>";
echo "$rowId$columnId";

if ($status == 0
   || ($status == 1
      && $updatedby == $_SERVER['PHP_AUTH_USER'])) {
   echo "<input type='checkbox' name='seats[]'"
      . " value='$rowId$columnId'></checkbox>";

From PHP, we step back a bit and discuss the JavaScript part of the code which is as important as the PHP scripts.

	function reserveSeats() {

		var selectedList = getSelectedList('Reserve Seats');

		if (selectedList) {
			if (confirm('Do you want to reserve selected seat/s ' + selectedList + '?')) {
				document.forms[0].oldStatusCode.value=0;
				document.forms[0].newStatusCode.value=1;
				document.forms[0].action='bookseats.php';
				document.forms[0].submit();
			} else {
				clearSelection();
			}
		}
	}

	function cancelSeats() {

		var selectedList = getSelectedList('Cancel Reservation');

		if (selectedList) {
			if (confirm('Do you want to cancel reserved seat/s ' + selectedList + '?')) {
				document.forms[0].oldStatusCode.value=1;
				document.forms[0].newStatusCode.value=0;
				document.forms[0].action='bookseats.php';
				document.forms[0].submit();
			} else {
				clearSelection();
			}
		}
	}

	function confirmSeats() {

		var selectedList = getSelectedList('Confirm Reservation');

		if (selectedList) {
			if (confirm('Do you want to confirm reserved seat/s ' + selectedList + '?')) {
				document.forms[0].oldStatusCode.value=1;
				document.forms[0].newStatusCode.value=2;
				document.forms[0].action='bookseats.php';
				document.forms[0].submit();
			} else {
				clearSelection();
			}
		}
	}

You will notice that there are three main functions (reserveSeats(), cancelSeats() and confirmSeats()) for modifying a seat’s status.

These scripts submit the values of the HTML FORM in seats.php to another PHP page named bookseats.php. This bookseats page is particularly interested in three FORM parameters from the seats page namely, oldStatusCode, newStatusCode and the seats array generated by the checkboxes.

An example of the request would be:

oldStatusCode=0
newStatusCode=1
seats[A1,A2,B3]

Which means Reserve the currently Available A1, A2 and B3 seats.

It is important to note the behavior of checkboxes in HTML. Only the values of “selected” checkboxes get submitted when you make a POST request. You can retrieve request parameters in PHP using $_GET for GET requests and$_POST for POST requests.

The bookseats page is divided into two parts. The first part checks whether the seats are still in the same state (no changes were done to the seats by another user while we were busy doing something else) prior to making any updates.


// dynamically build select statement

$selectQuery = "SELECT rowId, columnId from seats where (";
$count = 0;
foreach($_POST['seats'] AS $seat) {
if ($count > 0) {
$selectQuery .= " || ";
}
$selectQuery .= " ( rowId = '" . substr($seat, 0, 1) . "'";
$selectQuery .= " and columnId = " . substr($seat, 1) . " ) ";
$count++;
}

$selectQuery .= " ) and status = $oldStatusCode";
if ($oldStatusCode == 1) {
$selectQuery .= " and updatedby = '$user'";
}

// execute select statement
$result = mysql_query($selectQuery);

$selectedSeats = mysql_num_rows($result);
if ($selectedSeats != $count) {
$problem = "
<h3>There was a problem executing your request. No seat/s were updated.</h3>
";
die ($problem);
}

If the system detects any concurrent updates then it stops with step 1, displays an error message to the user and does not proceed with step 2.

The second part performs the actual database update after necessary checks from step 1 have been processed.


// prepare update statement
$newStatusCode = $_POST['newStatusCode'];
$oldStatusCode = $_POST['oldStatusCode'];

$updateQuery = "UPDATE seats set status=$newStatusCode, updatedby='$user' where ( ";
$count = 0;
foreach($_POST['seats'] AS $seat) {
 if ($count > 0) {
 	$updateQuery .= " || ";
 }
 $updateQuery .= " ( rowId = '" . substr($seat, 0, 1) . "'";
 $updateQuery .= " and columnId = " . substr($seat, 1) . " ) ";
 $count++;
}
$updateQuery .= " ) and status = $oldStatusCode";
if ($oldStatusCode == 1) {
 $updateQuery .= " and updatedby = '$user'";
}

// perform update
$result = mysql_query($updateQuery);
$updatedSeats = mysql_affected_rows();

if ($result && $updatedSeats == $count) {
 echo "
<h3>";
 echo "You have successfully updated $updatedSeats seat/s: ";
 echo "[";
 foreach($_POST['seats'] AS $seat) {
 	$rowId = substr($seat, 0, 1);
 	$columnId = substr($seat, 1);
 	echo $rowId . $columnId . ", ";
 }
 echo "]";
 echo "...</h3>
";
}

This application represents my first attempt into the PHP world and I must say that there are still a lot of improvements that can be added to this quick and dirty solution.

For one, the user can create his own login form instead of relying in the browser’s basic login form (shown earlier).

A logout option would be a very worthy addition and can be achieved by simply adding a link which calls the session_destroy() directive.

Styles and scripts can also be moved to external CSS and external JavaScript files.

Anyway, I still hope I was able to provide you enough details about PHP to serve as starting point.

The sources are available for download from GitHub at: https://github.com/polarvortex/tickets.git.

Comments (81)