• The Four Hundred
  • Subscribe
  • Media Kit
  • Contributors
  • About Us
  • Contact
Menu
  • The Four Hundred
  • Subscribe
  • Media Kit
  • Contributors
  • About Us
  • Contact
  • PHP Crash Course For RPG Developers

    February 23, 2016 Chris Ringer

    Suppose your boss tasks you with grabbing and processing some XML from an HTTP request to create a new customer order. If you have a “deer in the headlights” expression on your face, don’t fear, you don’t have to punt this request over the cubicle wall to the web developers. Without weeks or months of training, you can build a PHP script to retrieve the XML data that RPG can parse.

    What is PHP?

    PHP is a full-featured scripting language that runs on a web server. Just type your PHP code in a text editor (even Notepad), save it on the server, and point your browser to the script (“program”). The web server side compiles the script on-the-fly, runs it, and sends your script’s response back to the browser. Yes, this “on-the-fly every time” part sounds slow, but the PHP engine may cache commonly-used functions and scripts. Facebook uses PHP and can handle millions of hits per minute. The reality is that PHP is fast. Very fast.

    So how does PHP help the RPG developer? PHP can be a thin layer between the client (typically a browser or even another PHP script) and your existing proven RPG code on the IBM i, to get your business plugged into the web. In other words, in the MVC architecture, PHP is the controller, the router if you will, between the view (browser) and the model (RPG/DB2).

    PHP scripts can also be written to run from a command line, outside the context of a web server. This means PHP can perform such tasks as sending emails, generating barcode images, creating PDF documents, and consuming web services in an interactive or batch environment.

    And in case you didn’t know, the PHP engine is already pre-loaded on your IBM i, waiting for you to explore it.

    PHP vs. RPG

    When I first studied PHP back in 2007, I immediately noticed the syntax similarities of PHP and Java. But with PHP, I don’t have to ponder such questions as “Is my code threadsafe?” and “Are strings really immutable?” So my goal here is to help you learn PHP from an RPG perspective. I won’t be regurgitating basic PHP syntax, since hundreds of such articles and tutorials already exist and do a much better job than I ever could.

    How does PHP compare to ILE RPG? Look at Figure 1. Both can do database I/O. And PHP is supported on many operating systems, but if your focus is the IBM i, that may not be important to you. Furthermore, PHP supports object-oriented programming, but also allows RPG-style coding with procedures, so you do not need to understand OOP to use begin using PHP.

    However, in terms of built-in functionality, RPG will never match PHP. Even if you consider that about 50 RPG opcodes, such as CHAIN, might be considered function-like, PHP dwarfs RPG in this regard. PHP even has a function that returns the number of functions installed on your system. But then again, the volume of your proven existing RPG logic will always exceed what you begin coding now in PHP. The trick here is to marry the two, allowing PHP to tap into your existing RPG programs and service programs, and vice versa.

    I confess to not being a PHP guru, probably knowing about 300 functions. With PHP, you have to be careful to not reinvent the wheel and not write a function that already exists natively. To start learning basic PHP syntax, go to w3schools and bounce back here as you examine our sample scripts below. If you want to learn some advanced PHP, navigate to Hacking with PHP.

    What’s in a Name?

    Knowing you are a seasoned RPG professional, I will forgo the usual “Hello World” example. Let’s begin with a script file named genDocName.php containing a function named genDocName() that generates and returns a unique document name. A function in PHP is like an RPG subprocedure that allows parameters and optionally returns a value.

    NOTE: The numbers that begin each line of this example are not part of the PHP script. They are just references for this article.

     1  <?php
     2  function genDocName($prefix , $suffix = '', $extension = 'xml', $folder='/tmp') {
     3    // set file extension and folder 
     4    $extension = trim($extension) ;   
     5    if ( $extension == '' ) { 
     6       $extension = 'xml' ;
     7    }
     8    $folder = trim($folder) ;   
     9    if ( $folder == '' ) { 
    10       $folder = '/tmp' ;
    11    }
    12    // grab a value from HTTP headers 
    13    $requestID = '' ;
    14    if ( isset($_SERVER['UNIQUE_ID']) ) {   // Vnrx4X8AAAEAAAXQEgQAAAAj
    15       $requestID = $_SERVER['UNIQUE_ID'] ; 
    16    } elseif ( isset($_SERVER['REMOTE_PORT']) ) {   // 52844
    17       $requestID = $_SERVER['REMOTE_PORT'] ;  
    18    }
    19    // build unique doc name and exit when not found on the IFS
    20    do {
    21       $rand = $requestID . mt_rand() ; 
    22       $docID = uniqid($rand, true) ; 
    23       $docName = $folder . '/' . 
    24                  trim($prefix) . '_' . 
    25                  hash('sha1',$docID) . '_' .
    26                  trim($suffix) . '.' . $extension ; 
    27    } while ( file_exists($docName) ) ;
    28    return $docName ; 
    29  }
    30  ?>
    

    Breaking this down line by line:

    1  <?php
    

    The <?php tag indicates the beginning of the script.

    2  function genDocName($prefix , $suffix = '', $extension = 'xml', $folder='/tmp') {
    

    The function name is genDocName().The body of the function is between the open brace that ends this line and the closing brace in line 29. Four string parameters are defined. In RPG terms, all are passed by value. You can change a parameter value inside the function, but that value is not passed back to the caller. $suffix, $extension, and $folder are optional parameters. If not passed, they are assigned the default values that follow the equal signs. As in RPG, once an optional parameter is defined, the subsequent parameters must also be defined as optional. However, unlike RPG procedures, PHP scripts can reference unpassed parameters. A similar RPG prototype is:

    D genDocName...                          
    D                 Pr           500a   Varying   
    D  iPrefix                     100a   Varying Value 
    D  iSuffix                     100a   Varying Value Options(*NoPass)
    D  iExtension                   20a   Varying Value Options(*NoPass)
    D  iFolder                     100a   Varying Value Options(*NoPass)
    

    3    // set file extension and folder 
    

    As with RPG, double slashes begin a comment that extends through the remainder of the line.

    4    $extension = trim($extension) ;   
    

    trim() whitespace from the extension parameter.

    5    if ( $extension == '' ) { 
    6        $extension = 'xml' ;
    7    }
    

    If no document extension value was passed, assign the default value ‘xml’. The closing brace indicates the end of the “if” statement.

    8     $folder = trim($folder) ;   
    9     if ( $folder == '' ) { 
    10       $folder = '/tmp' ;
    11    }
    

    This is similar code for setting the folder path for a document. This script assumes you have a /tmp folder defined on your system.

    12    // grab a value from HTTP headers 
    13    $requestID = '' ;
    

    In line 13, the $requestID variable is set to a default value, an empty string, in case none of the “if” statements are true. I prefer this method to coding a final else condition because I think it makes the code more readable and maintainable.

    14    if ( isset($_SERVER['UNIQUE_ID']) ) {   // Vnrx4X8AAAEAAAXQEgQAAAAj
    15       $requestID = $_SERVER['UNIQUE_ID'] ; 
    16    } elseif ( isset($_SERVER['REMOTE_PORT']) ) {   // 52844
    17       $requestID = $_SERVER['REMOTE_PORT'] ;  
    18    }
    

    Now things are getting interesting. This code grabs some value from the HTTP request to increase the odds of the document name being unique. The isset() function returns true if a variable has both a memory address and a value (i.e., it isn’t null). Similar RPG code is:

    If (%Addr(parm) <> *NULL ) ;    // for an *OMIT parameter 
    If ( Not %NullInd(DB2Field) ) ;  // for a nullable table column 
    

    $_SERVER is a predefined prepopulated array that contains HTTP headers for the current request. I’ve always thought of HTTP headers as the RPG program status data structure (PSDS) and Retrieve Job Attributes (RTVJOBA) of the web. One of the values that may be in that array is a unique request identifier. Some web servers are configured to generate this value, so if this value is available, use it. Otherwise, try to use the port number of the client making the HTTP request. Example values can be seen in the comments in the script above.

    19    // build unique doc name and exit when not found on the IFS
    20    do {
    

    This is the start of a do/while loop, and it will execute at least once. In RPG this is a DOU loop.

    21       $rand = $requestID . mt_rand() ; 
    

    Here the $requestID is concatenated to a random integer. The function mt_rand() returns a random integer, such as 45310420. In PHP, a dot is the string concatenator, whereas RPG uses the plus sign. Perhaps you are expecting a compiler error on this line of code. After all, in RPG we have to use %Char() or %EditC() to convert (“cast”) numbers to strings before concatenating them. PHP casts variables of all types to strings automatically when needed.

    22       $docID = uniqid($rand, true) ; 
    

    The uniqid(”, true) function generates a predominantly unique string of 23 characters based on the current system time in microseconds. (This is not guaranteed to be unique if multiple processes call it at exactly the same time). The “$rand” value is prepended to this “unique” value, resulting in a string value similar to this one:

    Vnrx4X8AAAEAAAXQEgQAAAAj453104205654d1f04c5054.09600590 
    

    23       $docName = $folder . '/' . 
    

    Now we build the full document name, beginning with the folder. Remember that the dot is the string concatenator in PHP. Lines 23 to 26 are one statement in this script.

    24                  trim($prefix) . '_' . 
    

    Trim the blanks (whitespace) from the document name prefix and append an underscore.

    25                  hash('sha1',$docID) . '_' .
    

    The hash() function creates a message digest, a one-way string, from the $docID using the sha1 algorithm, which returns a 40-byte hexadecimal string. Again, append an underscore onto the end. Now, could I have just used the $docID in line 22 in the document name? Yes, but hash() has advantages. It generates a string that is safe to be used in a file name because: 1) it will never contain characters like /:*?<>; and 2) it will not accidentally generate inappropriate words.

    26                  trim($suffix) . '.' . $extension ; 
    

    Append the trimmed suffix, a period, and the extension.

    27    } while ( file_exists($docName) ) ;
    

    This closing brace completes the body of the do loop. The condition follows. If a document of the generated name does happen to be in that folder, file_exists() returns true and the loop repeats. I would expect this condition to be false and exit the loop on the first pass.

    28    return $docName ; 
    

    Returns a string containing a document name to the caller. The document name will look something like this:

    /tmp/OrderSubmit_b2268af34d2d4d9deb4dd744a255e7d1c4e8d0f6_Request.xml
    

    29  }
    

    The closing brace of the function.

    30  >
    

    The end of the PHP script. This is optional. In fact, some editors complain if you type it. If the closing tag is omitted, the PHP compiler will assume this when it reaches the end of file.

    Test the Shiny New Function

    In order to test this nice little function, we are going to write some “mainline” code in a text file named testGenDocName.php in the same IFS folder. See Figure 2 for the folder structure.

    1  <?php
    2  require_once 'genDocName.php' ; 
    3  define('PREFIX','OrderSubmit') ; 
    4  define('BR','<br />') ; 
    5  for ($cnt = 1; $cnt <= 10; $cnt++) {
    6     $docName = genDocName(PREFIX, 'Request') ;
    7     echo $docName, BR ; 
    8  } 
    9  echo BR, 'All Done!' ; 
    

    Breaking this down line by line:

    1  <?php
    

    Again the script code begins after the <?php tag.

    2  require_once 'genDocName.php' ; 
    

    This copies our function into our mainline script. RPG developers know it better as “/INCLUDE” or “/COPY”:

    /INCLUDE *LIBL/QCYPSRC,GENDOCNAME
    
    3  define('PREFIX','OrderSubmit') ; 
    4  define('BR','<br />') ; 
    

    We define two constants, PREFIX and an HTML br line break. Think of these as D-Spec Const statements:

    D PREFIX          c                   Const('OrderSubmit') 
    D BR              c                   Const('<br />') 
    
    5  for ($cnt = 1; $cnt <= 10; $cnt++) {
    

    This is a for loop, counting from 1 to 10. In RPG we code:

    For $cnt = 1 by 1 to 10 ;
    

    6     $docName = genDocName(PREFIX, 'Request') ; 
    

    Call the genDocName() function. The returned string is stored in $docName.

    Notice that I don’t have to predefine the variable $docName in this code. It’s implied to be a string because the function returns a string. This $docName is not the same variable as the one on line 23 of the genDocName() function. When a functions returns, all of its variables no longer exist.

    7     echo $docName, BR ; 
    

    Send the document name and a carriage return/line feed back to the browser (client) as output. echo accepts a list of strings separated by commas. You can also echo integers, floats, and boolean data types, which automatically cast to strings.

    8  } 
    

    The closing brace of the for loop.

    9  echo BR, 'All Done!' ; 
    

    Just a confirmation that it all worked.

    To check the uniqueness of the $docName returned by genDocName(), I modified the test script slightly to insert the $docName into a DB2 table in a 200,000 count loop. I opened four Chrome browser windows with six tabs each and ran the 24 test scripts simultaneously, which overall inserted 19,400 rows per second. The table had zero duplicates. To run this test, point your browser to this URL:

    http://YourSys:10088/test/testGenDocName.php
    

    The “A-ha!” Moment

    Hopefully you understand these sample scripts, but you can’t exactly run to your manager and exclaim that they will help your company ship product out the door. Or can you? Study Figure 3 to grasp the high-level flow from the HTTP request to the response. Our goal is to pull the XML from the body of an HTTP request. With only a few more lines of PHP code, we can complete this task. Our final script file is named CreateSalesOrder.php. A given client will post the HTTP request to this script, located in the same test folder as in Figure 2.

    Here is the script.

     1 <?php
     2 require_once 'genDocName.php' ; 
     3 $docName = genDocName('OrderSubmit','Request') ;
     4 $rawPostXmlData = file_get_contents('php://input') ; 
     5 file_put_contents($docName, $rawPostXmlData) ; 
     6 $connArray = array('i5_naming'=>DB2_I5_NAMING_ON,       // Use *LIBL 
     7                    'i5_commit'=>DB2_I5_TXN_NO_COMMIT) ; // SQL Commitment *NONE
     8 $conn = db2_connect('*LOCAL','','', $connArray) ;       // User QTMHHTTP 
     9 // Pseudo-code here to call RPG passing the document name
    10 // CALL YourStoredProc($docName, $OutResponse) ; 
    11 echo $OutResponse ; 
    

    Breaking this down line by line:

    1 <?php
    2 require_once 'genDocName.php' ; 
    3 $docName = genDocName('OrderSubmit','Request') ;
    

    This code is the same as before to call the genDocName() function.

    4 $rawPostXmlData = file_get_contents('php://input') ; 
    

    This retrieves the body of the HTTP request, XML in our case (see Figure 3), and stores that string in variable $rawPostXmlData. How easy is that?

    5 file_put_contents($docName, $rawPostXmlData) ;
    

    The XML string is saved into a new document in the /tmp folder of the IFS. Again, how easy is that?

    6 $connArray = array('i5_naming'=>DB2_I5_NAMING_ON,       // Use *LIBL 
    7                    'i5_commit'=>DB2_I5_TXN_NO_COMMIT) ; // SQL Commitment *NONE
    8 $conn = db2_connect('*LOCAL','','', $connArray) ;       // User QTMHHTTP 
    

    Connect to the local IBM i. When the second parameter of db2_connect is an empty string, the connection user profile is QTMHHTTP and the third parameter, the password, is ignored. Please conform to your company’s security policy here.

    9  // Pseudo-code here to call RPG passing the document name 
    10 // CALL YourStoredProc($docName, $OutResponse) ; 
    

    Your RPG program, probably defined as an external SQL Stored Procedure, is called to parse the IFS XML, passing in the name of the new document and passing back a response string as an OUT parameter. See example 2 here.

    If you will be promoting this RPG program through various test libraries on a development box, you may want to execute the SQL “Create Procedure” statement with option “External Name MYPGM” instead of “External Name MYLIB/MYPGM” so the *PGM object can be found via the library list. Otherwise, you may need to create the stored procedure in each test library.

    11 echo $OutResponse ; 
    

    The response string is sent back to the caller as an HTTP response.

    And there you have it. Using a few dozen lines of PHP code, your RPG is connected to the web. Hopefully this article has demonstrated the power of PHP and piqued your interest to learn more!

    In my next article, I plan to show you how to install the PHP engine and Apache web server on your laptop, including an IDE editor and debugger, so you can wow your friends at family gatherings. Until then if you want to kick the tires yourself, websites like this allow you enter and execute PHP code.

    Chris Ringer has been coding in RPG since 1989, focusing primarily on order fulfillment, pharmaceutical and manufacturing environments. In his spare time he enjoys running and doing triathlons. “I’m not fast, usually placing in the middle of the pack, but we’re just as competitive back there.”

    Share this:

    • Reddit
    • Facebook
    • LinkedIn
    • Twitter
    • Email

    Tags:

    Sponsored by
    New Generation Software

    FREE Webinar:

    Creating Great Data for Enterprise AI

    Enterprise AI relies on many data sources and types, but every AI project needs a data quality, governance, and security plan.

    Wherever and however you want to analyze your data, adopting modern ETL and BI software like NGS-IQ is a great way to support your effort.

    Webinar: June 26, 2025

    RSVP today.

    www.ngsi.com – 800-824-1220

    Share this:

    • Reddit
    • Facebook
    • LinkedIn
    • Twitter
    • Email

    Sponsored Links

    COMMON:  2016 Annual Meeting & Expo, May 15 - 18, in New Orleans! Great Power Systems event!
    System i Developer:  RPG & DB2 Summit - March 22-24 in Dallas. Check out the session grid!
    BCD:  Webinar - Getting Started with PHP on IBM i with Mike Pavlak. Feb 25

    The Rewards of IBM i Community Engagement Payday For The People Who Make The IBM i Go

    Leave a Reply Cancel reply

Volume 16, Number 04 -- February 23, 2016
THIS ISSUE SPONSORED BY:

Bug Busters Software Engineering
WorksRight Software
System i Developer

Table of Contents

  • PHP Crash Course For RPG Developers
  • SBMJOB, CALL, And Decimal Parameters
  • XML-INTO And Optional Elements

Content archive

  • The Four Hundred
  • Four Hundred Stuff
  • Four Hundred Guru

Recent Posts

  • Big Blue Raises IBM i License Transfer Fees, Other Prices
  • Keep The IBM i Youth Movement Going With More Training, Better Tools
  • Remain Begins Migrating DevOps Tools To VS Code
  • IBM Readies LTO-10 Tape Drives And Libraries
  • IBM i PTF Guide, Volume 27, Number 23
  • SEU’s Fate, An IBM i V8, And The Odds Of A Power13
  • Tandberg Bankruptcy Leaves A Hole In IBM Power Storage
  • RPG Code Generation And The Agentic Future Of IBM i
  • A Bunch Of IBM i-Power Systems Things To Be Aware Of
  • IBM i PTF Guide, Volume 27, Numbers 21 And 22

Subscribe

To get news from IT Jungle sent to your inbox every week, subscribe to our newsletter.

Pages

  • About Us
  • Contact
  • Contributors
  • Four Hundred Monitor
  • IBM i PTF Guide
  • Media Kit
  • Subscribe

Search

Copyright © 2025 IT Jungle