RPG and PHP
This was originally posted on my old website in January 2011. It’s dated, but I thought some folks might still find it useful.
I recently attended an iSeries Users Group meeting where we saw a demonstration of the installation of the Zend Framework on an IBM iSeries machine.
Most of the folks there were primarily RPG people who had many basic questions about PHP (the language used by the Zend Framework. Since I’ve been programming in PHP since the mid-’90s and RPG since the System/34 and System/38 in 1980, I thought I might be able to shed some light on the similarities and differences of the two platforms.
To that end, I’ve written a pair of programs. One in RPG with a Display File and the other in PHP using HTML. Neither is what I would consider a “good” program. Neither is written the way I would write a “REAL” program. What they are is just about as isomorphic as I could make them and my hope is that RPG programmers will be able to look at them side-by-side and get some insights into what PHP is all about.
I’ve sprinkled them with comments that try to explain the syntax, compare the differences and point out the similarities.
Hopefully they will be of use.
First the tables…
addresses.pf – The physical file DDS for the RPG version
A UNIQUE
A R ADDRESSRCD
A
A ID 3S 0 TEXT('Address ID')
A FIRSTNAME 20A TEXT('First Name')
A LASTNAME 20A TEXT('Last Name')
A ADDRESS1 50A TEXT('Address Line 1')
A ADDRESS2 50A TEXT('Address Line 2')
A CITY 20A TEXT('City')
A STATE 2A TEXT('State')
A ZIP 5S 0 TEXT('Zip')
A
A K ID
Code language: JavaScript (javascript)
addresses.sql – The SQL DDL for the PHP version
CREATE TABLE `example`.`addresses` (
`ID` INT( 3 ) NOT NULL AUTO_INCREMENT PRIMARY KEY ,
`LastName` VARCHAR( 20 ) NOT NULL ,
`FirstName` VARCHAR( 20 ) NOT NULL ,
`Address1` VARCHAR( 50 ) NOT NULL ,
`Address2` VARCHAR( 50 ) NOT NULL ,
`City` VARCHAR( 20 ) NOT NULL ,
`State` CHAR( 2 ) NOT NULL ,
`Zip` DECIMAL( 5 ) NOT NULL
) ;
Code language: JavaScript (javascript)
The only significant difference between the two is that the SQL version makes use of an auto-incrementing ID number. In the RPG Version I’ll use SETLL/READP to get the last ID number and increment it.
The Display Screen
exampled.dspf – the Display File
A*---------------------------------------------------------------
A* EXAMPLED - PHP/RPG Example - Display File
A*---------------------------------------------------------------
A* Written - 03Feb2010
A* By - George Alderton, Walkingstick Software, Inc.
A* https://www.wstick.dev
A*---------------------------------------------------------------
A* This is the DDS Display File counterpart to exampled.php
A*---------------------------------------------------------------
A DSPSIZ(24 80 *DS3)
A REF(*LIBL/ADDRESSES)
A*---------------------------------------------------------------
A* MenuSCN Record Format - An Entry Field, a Message and three
A* Cmd Keys (plus CF03 to exit). In
A* The PHP Example, we have three buttons.
A*---------------------------------------------------------------
A R MENUSCN
A CF03(03)
A CF09(09)
A CF10(10)
A CF11(11)
A 6 4'MENU'
A DSPATR(HI)
A
A 8 8'Record ID'
A +1'(for change and delete)'
A RECORDID 3S 0B +1
A 10 8'Enter a Record ID and'
A +1'press a Function Key...'
A 12 10'F9=Add'
A +2'F10=Change'
A +2'F11=Delete'
A 14 4'Message:'
A MESSAGE 40 O +1
A 50 COLOR(RED)
A 16 10'F3=Exit'
A*---------------------------------------------------------------
A* EntrySCN Record Format - The Data Entry Fields
A*---------------------------------------------------------------
A R ENTRYSCN
A MODE 10A O 6 4
A DSPATR(HI)
A
A 8 4'Address ID. . . . . .'
A 51 SID R O 8 28REFFLD(ID)
A N51 8 28'NEW'
A 9 4'Name (Last, First). .'
A SLASTNAME R B 9 28REFFLD(LASTNAME)
A SFIRSTNAMER B 9 50REFFLD(FIRSTNAME)
A 10 4'Address . . . . . . .'
A SADDRESS1 R B 10 28REFFLD(ADDRESS1)
A SADDRESS2 R B 11 28REFFLD(ADDRESS2)
A 12 4'City State ZIP. . . .'
A SCITY R B 12 28REFFLD(CITY)
A SSTATE R B 12 50REFFLD(STATE)
A SZIP R B 12 54REFFLD(ZIP)
A 14 4'Press ENTER to'
A MODEBTN 10A O +1
Code language: PHP (php)
exampled.php – the PHP/HTML Display Script
<!-- ---------------------------------------------------------------------- -->
<!-- exampled.php - PHP/RPG Example - HTML to Describe the Entry Screen -->
<!-- ---------------------------------------------------------------------- -->
<!-- Written - 03Feb2010 -->
<!-- By - George Alderton, Walkingstick Software, Inc. -->
<!-- ---------------------------------------------------------------------- -->
<!-- This is the PHP/HTML counterpart to EXAMPLED DSPF -->
<!-- ---------------------------------------------------------------------- -->
<!-- HTML SYNTAX - If you haven't figured it out by now, HTML Comments are -->
<!-- enclosed between "< ! - -" and "- - >" tags more or -->
<!-- less like CL uses */ and /* -->
<!-- HTML SYNTAX - HTML is made up of plain text and markup <tags> -->
<!-- Tags come in pairs that may be nested, but must balance -->
<!-- just like If/EndIf Select/EndSL Do/EndDo structures -->
<!-- in RPG. Look at the <html>, <head> and <title> tags -->
<!-- here. Then closing </html> tag is down at the end after -->
<!-- the closing </body> tag. -->
<html>
<head>
<title>PHP/RPG Example</title>
</head>
<body>
<!-- HTML and PHP - A DDS Display File communicates values between itself -->
<!-- and its RPG program by being an externally described -->
<!-- file and automatically having its fields shared by the-->
<!-- program. In this example, the two communicate by -->
<!-- having PHP code embedded inside the HTML. As the html-->
<!-- page is being sent to the browser, the PHP code is -->
<!-- executed and the results control or combine with the -->
<!-- HTML to form the final page. -->
<!-- PHP code is inserted inside a special <?php ?> tag -->
<!-- Here we have an example of the opening part of an If -->
<!-- statement. The closing bracket is a few lines down -->
<!-- the page. -->
<!-- MenuScn Record Format - An entry Field, a Message and three buttons. -->
<!-- In the DDS, we use Cmd Keys 9,10 and 11 for -->
<!-- Add, Change and Delete -->
<?php if ($ScreenToDisplay == 'MenuSCN') { ?>
<h1>Menu</h1>
<form name="menu" action="examplep.php">
Record ID (for change and delete): <input type="text" name="RecordID"/><br/><br/>
Enter a Record ID and press a button...<br/><br/>
<input type="submit" name="action" value="add">
<input type="submit" name="action" value="change">
<input type="submit" name="action" value="delete"/>
</form>
Message: <FONT <?php if ($MessageSeverity == 'ERROR') {echo "COLOR='RED'";}?> >
<?php echo $Message ?></FONT>
<?php } ?>
<!-- EntryScn Record Format - The data entry fields -->
<?php if ($ScreenToDisplay == "EntrySCN") { ?>
<?php $ModeBtn=$Mode ?>
<h1><?php echo $Mode?></h1>
<form name="EntrySCN" action="examplep.php">
<input type="hidden" name="action" value="Process<?php echo $Mode ?>"></input>
<table>
<tr><td>Address ID</td>
<td><?php if ($Mode=='Add') {echo 'NEW';} else {echo $sID;} ?>
<input type="hidden" name="sID" value="<?php echo $sID?>"/></td></tr>
<tr><td>Name (Last, First)</td>
<td><input type="text" name="sLastName" value="<?php echo $sLastName ?>" />
<input type="text" name="sFirstName" value="<?php echo $sFirstName ?>" /></td></tr>
<tr><td>Address</td>
<td><input type="text" name="sAddress1" value="<?php echo $sAddress1?>"/><br/>
<input type="text" name="sAddress2" value="<?php echo $sAddress2?>"/></td></tr>
<tr><td>City/State/Zip</td>
<td><input type="text" name="sCity" value="<?php echo $sCity?>"></input>
<input type="text" name="sState" value="<?php echo $sState?>"></input>
<input type="text" name="sZip" value="<?php echo $sZip?>"></input></td></tr>
</table>
<input type="submit" name="submit" value="<?php echo $ModeBtn ?> Record"></input>
</form>
<?php } ?>
</body>
</html>
Code language: PHP (php)
One of the differences between the two approaches is that when using RPG, the display file is an external object distinct from the RPG program that references it. In PHP, this HTML/PHP segment is really a part of the program script that is running.
In RPG, with an externally described display file, the fields are made available to the program and data is transfered via a file buffer. In PHP, since the display and program are the same, data is always available. We just need to print it where we need it when sending the the display to the browser. You can see <?php echo $var?>
code segments inserted in the HTML to supply the field data.
Finally, the Logic!
exampler.rpgle – The RPG Logic
H Option(*NoDebugIO)
A*---------------------------------------------------------------
A* EXAMPLER - PHP/RPG Example - RPG Progra,
A*---------------------------------------------------------------
A* Written - 03Feb2010
A* By - George Alderton, Walkingstick Software, Inc.
A*---------------------------------------------------------------
A* This is the RPG counterpart to examplep.php
A*---------------------------------------------------------------
FExampleD cf e WORKSTN
FAddresses uf a e k disk
D MessageSeverity...
D s 5
D
D Action s 20
D ScreenToDisplay...
D s 10
D AddedID s like(ID)
/free
// ------------------------------------------------------------------
// In web programming, a request is made from the web browser to the
// web server, the web server loads and runs the program and sends
// a new page to the browser and ends. There is no looping and the
// program does not stay loaded.
//
// In this RPG example, I've tried to implement a similar behavior.
// Inside the DoU *in03 loop, we process input from the last screen,
// set up the display for the next screen and display it.
// ------------------------------------------------------------------
DoU *in03;
Select;
When Action='add';
sID=0;
sLastName='';
sFirstName='';
sAddress1='';
sAddress2='';
sCity='';
sState='';
sZip=0;
ScreenToDisplay='EntrySCN';
Mode='Add';
When Action='ProcessAdd';
SetGT *hival AddressRcd;
ReadP AddressRcd;
If %eof;
ID = 1;
else;
ID += 1;
EndIf;
LastName=sLastName;
FirstName=sFirstName;
Address1=sAddress1;
Address2=sAddress2;
City=sCity;
State=sState;
Zip=sZip;
Write AddressRcd;
If %Error;
Message='Error Adding RecordÜ';
MessageSeverity='ERROR';
ScreenToDisplay='MenuSCN';
Else;
AddedID = ID;
Message='Record '+%EditC(AddedID:'4')+' Added.';
MessageSeverity='INFO';
ScreenToDisplay='MenuSCN';
EndIf;
When Action='change';
Chain RecordID AddressRcd;
If not %found;
Message='Record Not Found - Can''t ChangeÜ';
MessageSeverity='ERROR';
ScreenToDisplay='MenuSCN';
Else;
sID=ID;
sLastName=LastName;
sFirstName=FirstName;
sAddress1=Address1;
sAddress2=Address2;
sCity=City;
sState=State;
sZip=Zip;
ScreenToDisplay='EntrySCN';
Mode='Change';
EndIf;
When Action='ProcessChange';
Chain sID AddressRcd;
If not %found;
Message='Error Changing Record';
MessageSeverity='ERROR';
ScreenToDisplay='MenuSCN';
Else;
LastName=sLastName;
FirstName=sFirstName;
Address1=sAddress1;
Address2=sAddress2;
City=sCity;
State=sState;
Zip=sZip;
Update AddressRcd;
Message='Record Changed';
MessageSeverity='INFO';
ScreenToDisplay='MenuSCN';
Mode='Change';
EndIf;
When Action='delete';
Chain sID AddressRcd;
If not %found;
Message='Could Not DeleteÜ';
MessageSeverity='ERROR';
ScreenToDisplay='MenuSCN';
Else;
Delete AddressRcd;
Message='Deleted';
MessageSeverity='INFO';
ScreenToDisplay='MenuSCN';
Mode='Change';
EndIf;
Other;
ScreenToDisplay='MenuSCN';
EndSl;
If ScreenToDisplay='MenuSCN';
If MessageSeverity = 'ERROR';
*in50=*on;
Else;
*in50=*off;
EndIf;
ExFmt MenuSCN;
Select;
When *in09;
Action='add';
When *in10;
Action='change';
When *in11;
Action='delete';
EndSl;
EndIf;
If ScreenToDisplay='EntrySCN';
Action='Process'+Mode;
ModeBtn=Mode;
If Mode='Add';
*in51=*off;
Else;
*in51=*on;
EndIf;
ExFmt EntrySCN;
EndIf;
EndDo;
*inlr=*on;
/end-free
Code language: PHP (php)
examplep.php – The PHP Logic
<?php
// PHP VARIABLE NAMES all begin with "$" - CL uses "&"
// PHP VARIABLE NAMES are Case Sensitive
// PHP OBJECTS - PHP supports objects (like Java), but it is also procedural (like RPG)
// This example is written in a procedural style, with the occasional use
// of an object
// PHP ENVIRONMENT - PHP Doesn't have a built-in Database
// In this example, we're going to use a PDO (PHP Data Object)
// to connect to a MySQL database called "example"
$dbHost = "localhost";
$dbName = "example";
$dbUser = "dbuserid";
$dbPW = "password";
$dbh = new PDO("mysql:host=$dbHost;dbname=$dbName",$dbUser,$dbPW);
// PHP ARRAYS use square brackets "[]" - RPG ARRAYS use parentheses "()"
// PHP ARRAYS can be indexed by a string value
// PHP SYNTAX - All lines end with ";" - No need for a continuation character
// PHP ENVIRONMENT - $_REQUEST is a built-in array of values being received from the user's web browser
// This can be string from the URL or FORM elements. We're mostly interested in forms
$Action = $_REQUEST['action'];
$Message = "";
$MessageSeverity="";
// Unlike RPG programming, in web programming, the program does not stay in memory. A request is made from
// the broswser to the server, the program runs on the server, processing the request and sending the results
// back to the browser. Since the program does not stay loaded in memory, variables disappear after the result
// page is sent to the browser. In this example, I get around this limitation, by putting hidden fields on
// the display so when the the program is loaded the next time, the data will be available.
switch ($Action) { // PHP KEYWORDS - RPGLE SELECT = PHP switch
// Action "add" - set up Data Entry Screen for adding a new record
case "add": { // PHP KEYWORDS - PHP has no ENDxx, Block statements use "{" and "}" to denote block statements
// clear the data data entry screen fields
$sID = 0;
$sLastName = '';
$sFirstName = '';
$sAddress1 = '';
$sAddress2 = '';
$sCity = '';
$sState = '';
$sZip = 0;
// the next step is to display the "EntrySCN" in "Add" mode
$ScreenToDisplay="EntrySCN";
$Mode = "Add";
break; // PHP KEYWORDS - PHP treats the switch/case statement like a series of IFs.
// always use break, it takes us to the end of the switch and out of the case statements
}
// Action "ProcessAdd" - Process the data entry from the "add" data entry screen
case "ProcessAdd": {
// copy the data from the display into working fields
$sLastName = $_REQUEST['sLastName'];
$sFirstName = $_REQUEST['sFirstName'];
$sAddress1 = $_REQUEST['sAddress1'];
$sAddress2 = $_REQUEST['sAddress2'];
$sCity = $_REQUEST['sCity'];
$sState = $_REQUEST['sState'];
$sZip = $_REQUEST['sZip']*1;
// use SQL to insert the new record into the database
try {
$count = $dbh->exec(
"INSERT INTO addresses(LastName,FirstName,Address1,Address2,City,State,Zip) " .
" VALUES ('$sLastName', '$sFirstName', '$sAddress1', '$sAddress2', '$sCity', '$sState', $sZip)");
} catch (PDOException $e) {
echo $e->getMessage;
}
// the exec method, returns the number of records inserted, 0 are updated if there's an error
if ($count == 0) { //PHP OPERATIONS - "==" is a comparison, "=" is an assignment
// error - set an error message and re-direct back to the Menu
$Message = "Error adding record!" . $count;
$MessageSeverity="ERROR";
$ScreenToDisplay="MenuSCN";
} else {
// Record added - set a meessage and re-direct back to the Menu
$AddedID = $dbh->lastInsertID();
$Message = "Record $AddedID Added.";
$MessageSeverty="INFO";
$ScreenToDisplay = "MenuSCN";
}
break;
}
// Action "change" - set up the display to change a record.
case "change": {
// get Record ID from the menu screen
$RecordID = $_REQUEST['RecordID'];
$RecordID = $RecordID * 1; // PHP QUIRKS - PHP doesn't particularly care what kind of value a variable holds.
// We do. Multiplying by 1 here makes sure we have a number for use in our query.
// use SQL to read the selected record.
// The query method returns a statement object or a null object if no records were found
$stmt = $dbh->query("SELECT * FROM addresses WHERE ID=$RecordID");
$result = $stmt->fetch(PDO::FETCH_ASSOC);
if ($result == null) {
// error - set an error message and re-direct back to the Menu
$Message = "Record Not Found - Can't Change!";
$MessageSeverity = "ERROR";
$ScreenToDisplay = "MenuSCN";
} else {
// Record found - set up the Entry Screen for Change Mode
$sID = $RecordID;
$sLastName = $result['LastName'];
$sFirstName = $result['FirstName'];
$sAddress1 = $result['Address1'];
$sAddress2 = $result['Address2'];
$sCity = $result['City'];
$sState = $result['State'];
$sZip = $result['Zip']*1;
$ScreenToDisplay = "EntrySCN";
$Mode = "Change";
}
break;
}
// Action "ProcessChange" - Process the data entry from the "change" data entry screen
case "ProcessChange": {
// copy the data from the display into working fields
$sID = $_REQUEST['sID'] * 1; // PHP QUIRKS - Make sure we have a number in $sID
$sLastName = $_REQUEST['sLastName'];
$sFirstName = $_REQUEST['sFirstName'];
$sAddress1 = $_REQUEST['sAddress1'];
$sAddress2 = $_REQUEST['sAddress2'];
$sCity = $_REQUEST['sCity'];
$sState = $_REQUEST['sState'];
$sZip = $_REQUEST['sZip']*1;
// Use SQL to update the selected record
// PHP SYNTAX - In PHP, objects communicate error conditions by "throwing and exception"
// Exception are "caught" by wrapping code inside a "try/catch" block
// This is similar to a MonMsg in CL
try {
$count = $dbh->exec (
"UPDATE addresses " .
" SET LastName='$sLastName', FirstName='$sFirstName', " .
" Address1='$sAddress1', Address2='$sAddress2', " .
" City='$sCity', State='$sState', Zip=$sZip" .
" WHERE ID=$sID");
} catch (PDOException $e) {
echo $e->getMessage;
}
if ($count == 0) {
$Message = "Error changing record!" . $count;
$MessageSeverity="ERROR";
$ScreenToDisplay="MenuSCN";
} else {
$Message = "Record Changed.";
$MessageSeverty="INFO";
$ScreenToDisplay = "MenuSCN";
}
break;
}
case "delete": {
$RecordID = $_REQUEST['RecordID'];
$RecordID = $RecordID * 1; // PHP QUIRKS - Make sure we have a number in $sID
$count = $dbh->exec("DELETE FROM addresses WHERE ID=$RecordID");
if ($count == 0) {
$Message = "Could not delete!";
$MessageSeverity = "ERROR";
$ScreenToDisplay = "MenuSCN";
} else {
$Message = "Deleted.";
$MessageSeverity = "INFO";
$ScreenToDisplay = "MenuSCN";
}
break;
}
default: {
$ScreenToDisplay="MenuSCN";
break;
}
}
?>
// PHP SYNTAX - The "require_once" function includes another file into the source
// just like a "/COPY" does in RPG.
// In this example, we copy in our display module, in RPG it's
// a file in the F-Spec.
<?php require_once("exampled.php")?>
<?php $dbh = null; ?>
Code language: PHP (php)
Well, there it is. As I said before, these are far from being examples of good programming practices, but the should serve as an aid to conceptual translation.
I hope this will help you to start using PHP. On the iSeries, Linux, Windows or MacOS it’s available just about everywhere and can be a powerful addition to your tool box.