Brandon Checketts

Web Programming, Linux System Administation, and Entrepreneurship in Athens Georgia

Page 8 of 30

Quick PHP Script to Generate a Barcode

This is a really quick script I came up with to generate a Code-39 Barcode. Many thanks to
Matthew Welch who created the Free 3 of 9 Barcode Font. PHP’s imagettftext function makes this pretty simple.

The $barcode_font referenced below is available from the link above ‘3 of 9’ font. The $plain_font is just a font file that I copied from /usr/share/fonts/default/Type1/ on a Linux box.

This script create a nice looking barcode with the text underneath it like this:

< ?php

$number = isset($_GET['number']) ? $_GET['number'] : '';

$barcode_font = dirname(__FILE__).'/fonts/FREE3OF9.TTF';
$plain_font   = dirname(__FILE__).'/fonts/plain.pfb';

$width = 200;
$height = 80;

$img = imagecreate($width, $height);

// First call to imagecolorallocate is the background color
$white = imagecolorallocate($img, 255, 255, 255);
$black = imagecolorallocate($img, 0, 0, 0);

// Reference for the imagettftext() function
// imagettftext($img, $fontsize, $angle, $xpos, $ypos, $color, $fontfile, $text);
imagettftext($img, 36, 0, 10, 50, $black, $barcode_font, $number);

imagettftext($img, 14, 0, 40, 70, $black, $plain_font, $number);

header('Content-type: image/png');

imagepng($img);
imagedestroy($img);

?>

UPS-PHP Patch to Log Requests and Responses

UPS doesn’t seem to be too big of a fan of Perl or PHP. They provide some powerful functionality through their API’s and their documentation is sufficient, but doesn’t contain examples of anything except for Visual Basic or Java. Fortunately their is an open source project called UPS-PHP that aims to fill that gap by providing some classes for interacting with UPS’s APIs. The UPS-PHP project seems to have lost steam though as the latest updates were over a year ago.

The latest version of the UPS shipping API requires the user to go through a process of creating test transactions, then voiding some transactions in a sandbox environment prior to allowing you access to the production environment. You have to email them every request and response that you send and received for the test transactions as well as various images and HTML documents that were created from their responses.

I added some logging ability to the UPS-PHP class responsible for sending and receiving the responses. A patch is available here if anybody else wants to try it. You basically call the new methods setDebugDir() and setTransaction() on the ‘ups’ object. Those tell it which directory to log to, and which filename to use respectively.

Usage would look something like this:

    $debug_dir = "{$_SERVER['DOCUMENT_ROOT']}/upsdebug/".date('Y')."/".date('m');
    // make sure that $debugdir exists... Create it if necessary
    $upsConnect->setDebugDir($debug_dir);
    $upsConnect->setTransaction(time());
    $upsConnect->setTemplatePath('../../xml/');
    $upsConnect->setTestingMode(1); // Change this to 0 for production
    $upsVoid = new upsVoid($upsConnect);
    $upsVoid->buildRequestXML($ShipmentIdentificationNumber);

At this point your $debugdir would have two files in it. One with the XML request and the other with the XML response, suitable for zipping up and sending to UPS for approval.

PROCEDURE can’t return a result set in the given context

I ran into a problem today when dealing with a very simple SQL Query. The query simply calls a stored procedure on the MySQL server. This is a trivial app, so I was using the very basic mysql_connect(), mysql_query() functions. The result wasn’t being returned an mysql_error() was saying that the error was:

PROCEDURE db.procedure_name can't return a result set in the given context

Of course ‘db.procedure_name’ was the actual name of the procedure I was calling. Googling for the error seemed to indicate that the MySQL client library was old, but this is on a fairly modern CentOS 5.5 server with the php-mysql package at version 5.1.6.

After a bit of experimenting, I found that I was able to change to using the mysql improved versions of the PHP functions and that worked fine

     $dbconn = mysql_connect($dbhost, $dbuser, $dbpass);
     $result = mysql_query("CALL db.procedure_name('arg1', 'arg2', 'arg3')", $dbconn)
     $row    = mysql_fetch_assoc($result);

Becomes

    $dbconn = mysqli_connect($dbhost, $dbuser, $dbpass);
    $result = mysqli_query($dbconn, "CALLdb.procedure_name('arg1', 'arg2', 'arg3')");
    $row    = mysqli_fetch_assoc($result);

Note that changing from mysql_query to myqli_query needs the parameters reversed.

After making that change I’m able to run the stored procedure correctly.

Southeast Linux Fest Presentation on MySQL Replication

I was fortunate to be selected to give a presentation at the 2010 Southeast Linux Fest held this year in Greenville, SC. The topic was MySQL replication which I picked from a similar presentation I gave about about 1.5 years ago at my local LUG. I’ve configured plenty of replicated servers and I think that I understand it well enough to explain it to others.

The 2-hour presentation is about half slides and half demo. Throughout the course of the presentation I set up a simple master-slave. Then I add a second slave. Taking it a step farther I set up the three servers to replicate in a chain, and finally I configure them to replicate in a full circle so that changes made on one are propagated to all of the others. I intentionally do things that break replication at certain points to show some of the limitations and configurable features that can help it to work.

Slides for the presentation are available OpenOffice format.

The presentation was recorded, so hopefully the SELF team will have those videos available shortly.

Script to Import Static Pages into GetSimple CMS

I’ve recently been impressed with a very simple Content Management System called GetSimple. It provides just the very basics that allows a user to edit their own website content. For brochure sites with owners who don’t want the complexity of a larger CMS, I think it is pretty ideal.

When I develop a site though, I typically have a header and footer, and then all of the content pages exist as PHP files that simply include that header and footer. Converting a static site like that into the CMS takes a bunch of copy/pasting. I always try to avoid such tedious jobs, and so developed a script that will import those static pages into a GetSimple installation.

To run this script, I wanted to import a bunch of files in a ‘static’ directory where I had moved all of the static files to. I then ran this from the command line to import all of the content into GetSimple

# for file in `find static -type f`
> do
> ./getsimple_import_file.php $file
> done

The script is available as getsimple_import_file.php

It takes a little configuration before running it. It works by simulating the data that you would submit when creating the page through the web interface, so we have to fake the necessary session cookie. Uncomment the bit in the middle that will display your cookie and run the script once. You’ll need to copy your cookie name and value into the script before doing any actual imports.

Once you’ve done that, you will probably want to change the regular expression that attempts to grab the page title from your file. You may also want to manipulate how it figures the URL to use.

Feel free to post comments here if you found this useful, or made any changes you’d like to share with other users

Enabling HTTP Page Caching with PHP

I’ve been doing a lot of work on BookScouter.com lately to reduce page load time and generally increase the performance of the website for both users and bots. One of the tips that the load time analyzer points out is to enable an expiration time for static content. That is easy enough for images and such by using an Apache directive such as:

    ExpiresActive On
    ExpiresByType image/gif A2592000
    ExpiresByType image/jpg A2592000
    ExpiresByType image/png A2592000

But pages generated with PHP by default have the Pragma: no-cache header set, so that the users’ browsers do not cache the content at all. In most cases, even hitting the back button will generate another request to the server which must be completely processed by the script. You may be able to cache some of the most intensive operations inside your script, but this solution will eliminate that request completely. Simply add this code to the top of any page that contains semi-static content. It effectively sets the page expiration time to one hour in the future. So if a visitor hits the same URL within that hour, the page is served locally from their browser cache instead of making a trip to the server. It also sends an HTTP 304 (Not Modified) response code if the user requests to reload the page within the specified time. That may or may-not be desired based on your site.

$expire_time = 60*60; // One Hour
header('Expires: '.gmdate('D, d M Y H:i:s \G\M\T', time() + $expire_time));
header("Cache-Control: max-age={$expire_time}");
header('Last-Modified: '.gmdate('D, d M Y H:i:s \G\M\T', time()));
header('Pragma: public');

if ((!empty($_SERVER['HTTP_IF_MODIFIED_SINCE'])) && (time() - strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) < = $expire_time)) {
    header('HTTP/1.1 304 Not Modified');
    exit;
}   

Skipping the DROP TABLE, CREATE TABLE statements in a large mysqldump file.

I have a large table of test data that I’m copying into some development environments. I exported the table with a mysqldump which has a DROP TABLE and CREATE TABLE statements at the top

DROP TABLE IF EXISTS `mytable`;
CREATE TABLE `mytable` (
  `somecol` varchar(10) NOT NULL default '',
   ... other columns ...
  PRIMARY KEY  (`somecol`),
  KEY `isbn10` (`somecol`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

The problem is that the developer has altered the table and re-importing the test data would undo those changes. Editing the text file is impractical because of its size (500 MB gzipped). So I came up with this workaround which just slightly alters the SQL using sed so that it doesn’t try to drop or recreate the table. It comments out the DROP TABLE line, and creates the new table in the test database instead of the real database.

zcat bigfile.sql.gz |sed "s/DROP/-- DROP/"|sed "s/CREATE TABLE /CREATE TABLE test./"|mysql databasename

Sleeping for a random amount of time in a shell script

You can use the special $RANDOM environment variable to get a random number and divide it by the maximum number of seconds that you want to wait. Use the remainder as the number of seconds to sleep since it will always be between zero and the max you specified. This example will sleep anywhere between zero and 10 minutes (600 seconds)

 /bin/sleep/sleep   `/usr/bin/expr $RANDOM % 600`

Purists will note that it isn’t truly random. The maximum value for $RANDOM is 32767 which is not evenly divisible by most likely values, but it is close enough.

Installing SVN and Trac on a CentOS 5 server

Make sure that you have the RPMForge repository enabled. Install Subversion, mod_dav_svn, and trac. This will install a few required dependencies (ie: neon and some python utils)

# yum install subversion mod_dav_svn mod_python trac

Create a directory for your repositories, and an initial repository for testing, and create your htpasswd file. Then create a trac environment and set it up.

# mkdir /home/svn/
# svnadmin create testrepo
# chown -R apache:apache /home/svn/*
# htpasswd -c  /home/svn/.htpasswd brandon

#mkdir /home/trac/
# trac-admin /home/trac/ initenv
    ... answer questions as appropriate ...
# chown apache:apache /home/trac/*
# htpasswd -c  /home/svn/.htpasswd brandon

Add this to your Apache configuration in the relevant place (I like to put it under an SSL VirtualHost)

    <Location /svn>
        DAV svn
        SVNParentPath /home/svn/
        #SVNListParentPath on
        # Authentication
        AuthType Basic
        AuthName "RoundSphere SVN Repository"
        AuthUserFile /home/svn/.htpasswd
        Order deny,allow
        Require valid-user
    </Location>
    <Location /trac>
        SetHandler mod_python
        PythonHandler trac.web.modpython_frontend
        PythonOption TracEnv /home/trac
        PythonOption TracUriRoot /trac
        # Authentication
        AuthType Basic
        AuthName “MyCompany Trac Environment"
        AuthUserFile /home/svn/.htpasswd
        Require valid-user
    </Location>

Now test to make sure that you can view your test repository in a browser and that it prompts for a username and password as desired:

https://your-hostname/svn/testrepo/

You should retrieve a plain looking page that mentions the name of your repository and that it is at Revision 0

You should also be able to access your trac installation at

https://your-hostname/trac/

Customize your logo, change the home page, start making some tickets, using the wiki and get to work.

Pear Upgrade Installer

I was trying to install PHPUnit today, but the box wouldn’t allow me because the Pear Installer version wasn’t current. But there was no obvious way to upgrade the pear installer.

[root@ci /]# pear install phpunit/PHPUnit
Did not download optional dependencies: pear/Image_GraphViz, pear/Log, channel://pear.symfony-project.com/YAML, use --alldeps to download automatically
phpunit/PHPUnit requires PEAR Installer (version >= 1.8.1), installed version is 1.4.9
phpunit/PHPUnit can optionally use package "pear/Image_GraphViz" (version >= 1.2.1)
phpunit/PHPUnit can optionally use package "pear/Log"
phpunit/PHPUnit can optionally use package "channel://pear.symfony-project.com/YAML" (version >= 1.0.2)
phpunit/PHPUnit can optionally use PHP extension "json"
phpunit/PHPUnit can optionally use PHP extension "xdebug" (version >= 2.0.5)
No valid packages found
install failed

The trick is to install the PEAR package with –force to make it go through

[root@ci /]# pear upgrade --force PEAR
warning: pear/PEAR dependency package "pear/Archive_Tar" installed version 1.3.6 is not the recommended version 1.3.3
warning: pear/Archive_Tar requires PEAR Installer (version >= 1.5.4), installed version is 1.4.9
downloading PEAR-1.9.0.tgz ...
Starting to download PEAR-1.9.0.tgz (291,634 bytes)
.............................................................done: 291,634 bytes
downloading Archive_Tar-1.3.6.tgz ...
Starting to download Archive_Tar-1.3.6.tgz (17,600 bytes)
...done: 17,600 bytes
upgrade ok: channel://pear.php.net/Archive_Tar-1.3.6
upgrade ok: channel://pear.php.net/PEAR-1.9.0
PEAR: Optional feature webinstaller available (PEAR's web-based installer)
PEAR: Optional feature gtkinstaller available (PEAR's PHP-GTK-based installer)
PEAR: Optional feature gtk2installer available (PEAR's PHP-GTK2-based installer)
To install use "pear install pear/PEAR#featurename"

From there, you can continue on the the PHPUnit Install

« Older posts Newer posts »

© 2026 Brandon Checketts

Theme by Anders NorenUp ↑