juneih

Sep 092011
 

You would be forgiven for thinking that experts never do the boring beginners stuff. Surely, the football player getting paid X fantazillion gold dubloons a week (yes it is a figure so large it is measured by the week), surely he with all that skill and money, does not practise passing? Or ball receiving? But he does – in fact that is why he is an expert.

We are all in different places on our way to glory, stardom and general PHP-related insight – but what we have in common on this path could be that we have to keep practising the fundamentals. It is never a bad idea to re-read that man page, or to get more background information on things you already know. Since last time you visited that man page, you have learned new stuff which will give you more insight or a different perspective this time around. Often I find I have misunderstood some concepts, and repetition helps correcting a point of view about to go askew.

Here is a small example to get you started: did you know break[1] accepts parameters? Neither did I. Think about it. Why would you even look up break, you have used break hundreds of times before, you know how it works and when you should use it. In fact it is so basic you would think it does not have much of an entry – a quick lookup from vim in the PHP-docs [2] though – and tada – there it is. White on black: break accepts an optional parameter to break out of zero, one or more loop-flavour or switch. You know you will be needing it shortly.

Like that football player spending a late practise shooting at the goal, so should we practise our  fundamentals. I’m passing this on to bjori next month – he’ll give you one of his to ponder.

[1]   http://php.net/break
[2] You can have unix man pages for PHP functions integrated in Vim – so you don’t even have to waste bandwidth. Awesome. http://bjori.blogspot.com/2010/01/unix-manual-pages-for-php-functions.html

Jan 242011
 

The PHP competence group is proud to announce that our company Redpill Linpro will be Gold Sponsors of this year’s Symfony Live conference in Paris. We have instigated this sponsoring as we are, and have been, proud users of the framework for our customers for years. Pages we have created with symfony include everything from media based and content driven applications, to membership systems, and even SMS-handling. This is one of our ways of contributing back to a great OSS PHP project.

We are in the planning phase as of now, and have not yet decided what we should do to get attention at the conference, but there will definitely be booth-boys, give-aways and probably a competition. Do you have any booth attraction top tips for us?

Do not forget to check out our main Symfony contributor’s post about the sponsoring on his own blog.

Oct 022009
 

During development of a new web application that required users to log in, we had problems with users being logged out too soon. The cookies were configured to end when the sessions did, but the max life time given to sessions turned out to be completely ignored.

The application had a standard LAMP environment, the key element as we will see, being the OS: the Debian based distro Ubuntu Hardy.

How PHPs garbage collection handles sessions

The main issue behind our problem turned out to be garbage collection. PHPs garbage collection also handles sessions. This is necessary because sessions are usually stored as flat files at a given location, and since each session has a unique id they will accumulate into quite a large amount over time.

During the start up over every session, there is a chance the session garbage collection will run as well. Session garbage collection deletes all session files if they are older than the set session max life time. The default probability for a new session also running session garbage collection is one in a hundred, this probability is configurable in php.ini, along with the location of the session files.

The problem

The Ubuntu server running our web application was also running an additional application which did not always have the same PHP configuration. Apache is fully capable of this when using virtual hosts – the point is, the max life time changes were not done in any of the php.ini files. Despite of this, that is exactly were there is a clue to what was happening. The PHP version we are using is an Ubuntu/Debian package, which has some changes to the original PHP default setup. In all of the php.ini files, in the SESSION section just above the garbage collection probability setting, we found that the session garbage collection by default  was actually turned off. The reason given is Debians strict permissions on the default session save path. Furthermore, the section informs that PHPs garbage collection has been replaced by a cron job.

A closer inspection of the cron job revealed this to be true, every half hour, it will look for a maxlifetime setting in all php.ini files it can find, see if it has found a value larger than its default of 24 minutes, and go on to delete all session files in the default session save path older than this value. In our case our maxlifetime settings were not in either of the ini files, which means the default value of 24 minutes was effective.

The solution

The cron job calls /usr/lib/php5/maxlifetime to determine how old sessions can be before they can be deleted, and in this file it is possible to set a new default max life time corresponding to the highest value set in either your web app or in an Apache virtual host configuration.

If you are uneasy with the OS entering territory on what arguably should be PHPs business, you can of course also remove or comment the cron job and activate PHPs session garbage collector instead.

Apr 142009
 

DB2 is one of IBM’s database server systems, it comes in many flavours, and runs on many platforms including Linux and Windows, but traditionally on IBM’s own mainframes. DB2 has a long history, so if you’re in the business chances are you’ll need to work with it one day.

We bumped into it when we had to connect to said database across both platforms and network. Here’s an account of our experience with it.


The problem

A web application in a LAMP environment (Ubuntu Hardy, Apache 2, MySQL5 and PHP 5.2), where login should be done using a centralised database – the already mentioned IBM DB2, on IBM System i.  As with DB2, System i has a long history, so don’t be surprised if it’s referred to by previous names such as AS/400, or eServer iSeries.

The solution

Our first attempt involved installing DB2 express on the same server as the web application, by using the ibm_db2 driver we got a connection that worked, but we were denied access with the following message:

An attempt to connect to a host failed due to a missing DB2 Connect product or invalid license. SQLSTATE=42968

DB2 Connect is apparently part of some DB2 products, but not DB2 express. A long call to IBM eventually established that we probably needed to pay for this approach to work.

Second attempt

Luckily there is another way to connect to DB2 – using ODBC. Which means we need an ODBC driver for DB2. IBM calls System I Access a connectivity product, and its web page goes on to say: “The entire focus of System i Access is to deliver the strengths and capabilities of System i to the desktop through easy-to-use screens and wizards”. In short, it’s a bundle that includes a 5250 emulator, an EDRS driver, and what we need: an ODBC driver.

  • Get ODBC: We need an ODBC manager and PHP support for it. Download and install it by apt getting unixodbc, unixodbc-bin, php5-odbc and libstdc++5.
  • Get System i Access: We also need an ODBC driver for DB2, available for dowload free of charge as an RPM package from http://www-03.ibm.com/systems/i/software/access/linux/index.html. Remember to choose the correct version for your system (32-bit or 64-bit). To install it you can for instance run the following:

$ sudo rpm -ivh –nodeps iSeriesAccess-5.4.0-1.6.i386.rpm

It will be set up under the catalogue /opt/ibm/

The ODBC manager needs to know about the odbc driver and what to say to it. This is configured in two files: odbc.ini and odbcinst.ini. If they haven’t already been created by installing System i Access, they should be created in the /etc folder.

odbc.ini should look something like this:

[DB2CONN]
Description             = iSeries Access ODBC Driver
Driver                  = iSeries Access ODBC Driver
Driver64                = iSeries Access ODBC Driver 64-bit
System                  = 127.0.0.1
UserID                  = ELVIS
Password                = PRICILLA
Naming                  = 0
DefaultLibraries        = HAWAII,QGPL,qiws
Database                = GRACELAND
ConnectionType          = 0
CommitMode              = 2
ExtendedDynamic         = 1
DefaultPkgLibrary       = QGPL
DefaultPackage          = A/DEFAULT(IBM),2,0,1,0,512
AllowDataCompression    = 1
MaxFieldLength          = 32
BlockFetch              = 1
BlockSizeKB             = 128
ExtendedColInfo         = 0
LibraryView             = 0
AllowUnsupportedChar    = 0

Each connection has its own ini section. The following parameters are the ones you are most likely to change:

  • Driver: has to match a section in odbcinst.ini, which we’ll get to shortly.
  • System: The ip adress to where the db2 is
  • UserID
  • Password
  • DefaultLibraries: QGPL and qiws are standard, but most likely you need extra libraries
  • Database: The name of the database you want to connect to

odbcinst.ini should look something like this:

[iSeries Access ODBC Driver]
Description     = iSeries Access for Linux ODBC Driver
Driver          = /opt/ibm/iSeriesAccess/lib64/libcwbodbc.so
Setup           = /opt/ibm/iSeriesAccess/lib64/libcwbodbcs.so
NOTE1           = If using unixODBC 2.2.11 or later and you want the 32 and 64-bit ODBC drivers to share DSN’s,
NOTE2           = the following Driver64/Setup64 keywords will provide that support.
Driver64        = /opt/ibm/iSeriesAccess/lib64/libcwbodbc.so
Setup64         = /opt/ibm/iSeriesAccess/lib64/libcwbodbcs.so
Threading       = 2
DontDLClose     = 1
UsageCount      = 1

[iSeries Access ODBC Driver 64-bit]
Description     = iSeries Access for Linux 64-bit ODBC Driver
Driver          = /opt/ibm/iSeriesAccess/lib64/libcwbodbc.so
Setup           = /opt/ibm/iSeriesAccess/lib64/libcwbodbcs.so
Threading       = 2
DontDLClose     = 1
UsageCount      = 2

Notice there’s no mention of port anywhere. You cannot configure this, the port is 8471 so make sure it’s open in both ends. Also note that the Driver and Driver64 parameters in odbc.ini matches the two sections in odbcinst.ini

 

Using the connection
The setup should be up and running now, so to use it in PHP we need to create a connection. A simplified connection can look something like this:

function getConnection()
{
$dsn             = sfConfig::get(‘db2_conn_dsn’, ‘DB2CONN’);
$user            = sfConfig::get(‘db2_conn_usr’, ‘ELVIS’);
$pwd             = sfConfig::get(‘db2_conn_pwd’, ‘PRICILLA’);

$connection = odbc_connect($dsn, $user, $pwd) or die ("Can not connect to $dsn.");

return $connection;
}

An example of a simple login query that uses a stored procedure from the hawaii library:

function checkUserNameAndPassword($username, $password)
{

$conn            = getConnection();
$user            = false;
if ($conn)
{
$sql       = utf8_decode("{CALL HAWAII.LOGIN(‘$username’, ‘$password’)}");
$callstore = odbc_exec($conn, $sql) or die ("Can not perform query");
$user    = odbc_fetch_array($callstore, 1);

odbc_close($conn);
}
return $user;
}

For debugging purposes, a useful way to output the result from an odbc_exec can be found in one of the comments at: http://no.php.net/odbc_exec

 

Here’s a simplified example of how to insert data using a prepared statement:

function insertUser($user)
{

$conn = getConnection();

if($conn)
{
$sql  = utf8_decode("INSERT INTO HAWAII.ADDUSER (FNAME, LNAME, ADR1, CITY)");
$sql .= utf8_decode(" VALUES (?,?,?,?)");
$res  = odbc_prepare($conn, $sql) or die ("Can not prepare query");
$parameters = array(
utf8_decode($user['firstname']),
utf8_decode($user['lastname']),
utf8_decode($user['address']),
utf8_decode($user['city']));
if(!odbc_execute($res, $parameters)) die("Can not perform query");
odbc_close($conn);
}

return true;
}

You can also insert without using the prepare statement (by using odbc_exec), but we had trouble with character encoding. The example above worked for us.

 

Good luck!