In February, we blogged about the Zend 5250 Bridge announcement (“Zend Builds a Bridge”) and we thought it was time to show you how it works in practice. As we noted at the time, the Bridge isn’t a silver-bullet solution, but rather a toolkit of possibilities. At the recent ZendCon, we were delighted to see how many IBM i users were either already using or planning to use the Bridge. Some of those users have had tremendous success in modernizing their existing applications using the combination of Bridge and PHP. Hopefully, we can bring you some of those stories here or in the magazine in the coming months.
The Bridge API comes in two flavors—procedural and object-oriented (OO). For the purposes of this article, we’ll be using the procedural APIs, which while a little less efficient (they’re built on top of the OO APIs), are easier to get your head around if you’re unfamiliar with OO programming.
Before we look at the PHP code, it’s important to understand the basic process involved in using the Bridge. In essence, the Bridge APIs let you write a PHP script that will act as the “operator” of a 5250 terminal. Your script must sign in, call programs and interact with those programs. When you first start working with the APIs, one of the things to get used to is that the 5250 session's output is your script's input—and your script's output will become the 5250's input. Of course, in most cases, your script will take the data from the 5250 and, in turn, display it in the browser for the real user. The user then interacts with that display and your script processes the input and reacts to it, which may or may not involve responding to the 5250 session.
Zend supplies three demonstration programs with the Bridge, two of them interface to the same 5250 subfile program. One uses the procedural APIs and presents a very simple interface that mirrors the functionality of the 5250 display. The other uses the OO APIs and AJAX techniques to supply a more sophisticated UI. The third program is a green-screen emulator that serves two purposes. First, it can be used to allow programs that haven’t been refaced yet to coexist seamlessly with others that have. Second, it can be used to help you identify the necessary field identifiers you’ll need to build your Bridge scripts.
Figure 1 shows this emulator in action running the 5250 application that we’ll be using. Notice that when the cursor hovered over the first customer number in the subfile, the emulator told us the identifier for the field was outputField 8. We’ll use this information in our Bridge script. There are other ways to establish the field references, but this is a convenient, interactive approach. Check out the sidebar for the PHP code for a simple utility function you may find useful when developing your own Bridge applications.
The application shown here is a simple output-only subfile, which we’ll be converting to use a browser interface. Since Zend already supplied basic subfile demonstrations that mirror the current 5250 operation, we decided we'd do something a little different in our code. Rather than implement paging logic, we decided to effectively convert the application to a load-all subfile (see Figure 2)—only displaying it to the user when all data had been received. No changes were required in the 5250 application. Clearly this approach wouldn’t be practical if we anticipated thousands of records in the subfile, but for smaller volumes it’s an interesting possibility.
Time to look at some code. We won’t discuss the includes and other essential declarations you’ll find in the complete source; they’re fully covered in Zend's documentation and must be present in every Bridge script.
The first thing our code must do is to open a 5250 session using the zend_5250_open() function (A). The result of calling this function is a session resource ($bridge) we’ll use later to identify the session that we wish to interact with. Next, we actually open the session using zend_5250_connect() (B) passing it that session resource. Think of this as the process that goes on when you power up a real 5250 or first start an emulator session. In other words, it’s the process that results in a sign-on screen being displayed. We could have supplied the user ID and password at this stage, but we decided to follow the 5250 process fully to make the sequence clear. Next we test (C) to ensure our success in opening a valid connection. If all is OK, we proceed to sign on.
// Open a connection to the Zend 5250 Bridge, for a job with id 'SUBFTST2'
$bridge = zend_5250_open('SUBFTST2'); // <=A
$response = zend_5250_connect($bridge); // <=B
// report_5250_info($response);
if (false === $response) { // <=C
die(zend_5250_get_error().'<br />To try again please click <a href="' . $_SERVER['PHP_SELF'] . '">Click here</a>');
}
zend_5250_set_input_field($bridge, 0, "MYUSERID"); // <=D
zend_5250_set_input_field($bridge, 1, "MYPASSWORD");
$response = zend_5250_submit($bridge); // Press Enter // <=E
$checkField = zend_5250_get_output_field($response, 5); // make sure sign-on OK
if (trim($checkField['value']) == 'Press Enter to continue.') {
$response = zend_5250_submit($bridge); // <=F
}
To do this we must first “type” in the user ID and password we intend to use using the zend_5250_set_input_field() API (D). Notice that we supply both the session resource and the identifier number of the field we wish to set as parameters along with the actual values to be used. We could, of course, have asked the user for this information as the Zend demo programs do, but we wanted to demonstrate that the process can be invisible to the user. Once all of the input fields have been set, we must “press” the Enter key. We do this by calling the zend_5250_submit() function (E). The default action is Enter, so we don’t need any parameters other than the session resource. Notice the function returns us a response ($response) just as a real 5250 would when we press Enter. This contains all of the input and output fields that were sent to the display, including the value for the fields plus row and column information, color, etc.
As you know, if you already have a session signed on with a particular user ID, when you try to sign on again you’ll get a message telling you the message queue is already in use. Guess what—the same thing will happen here and we need to anticipate it. This is the purpose of the final piece of logic in this snippet (F). We need to check if the message “Press Enter to continue.” is present on the screen. Experimenting with the aforementioned Zend emulator will show you the field you want has the identifier 5 (it’s the sixth output field but numbering starts at 0). So we use the zend_5250_get_output() function to obtain the field contents and check if it matches. If it does, then we need to press Enter one more time to get to the initial screen.
The next thing we need to do is to set up to call the program we’re going to use. Just as we did with the sign-on information, we use the zend_5250_set_input_field() API to “key in” the data. The actual pressing of the Enter key takes place within the following do loop. The first time through, the field $action contains “ENTER” and so the submit specifies the enter key (G).
The response will contain the first page of the program's display. To display the output fields, we use the zend_5250_get_output_fields() API (H) to load all field data into the $fields array. Within this array, each field is represented as an associative array where the keys indicate the type of data (e.g. the value of the field, its length, row number, column number, etc.). To retrieve the appropriate data, we need to know which field numbers correspond to which fields. As we’ve already noted, we know output field 8 contains the customer number in the first row of the subfile. That means the name is in field 9, the city in 10, and the state and zip in 11 and 12 respectively. The second record in the subfile will be contained within fields 13 to 17, the third in 18 to 22, and so on. If the subfile page is full, the final row will be in fields 83 to 87 and they will be followed by the screen footer information.
Simple right? Well, there’s one tiny problem. While the row and column numbers for the footer information will remain consistent, their field numbers won’t. In the event that the page isn’t full, the field numbers for the footer will be lower and our logic must cater for this. We could have checked for the end of the subfile by monitoring the row and column, but instead we opted to use the fact that the display “F3=Exit” will always follow the last zip code field on the screen. This means all we have to do is to check if the first field of the new row contains the “F3=...” information in order to determine that we’ve processed all the subfile data on the page.
Now, let’s look at the mechanics of how it’s achieved. Before entering the while loop, we set the array index ($fieldIndex) equal to the constant firstCustNo (which currently is set to the value 8, the field number of the first customer number). Notice the while loop is conditioned (I) to end as soon as the value of the current field is “F3=Exit”. For those of you unfamiliar with PHP, the field reference “$fields[$fieldIndex]['value']” may be a little confusing. PHP uses the square brackets ([ ]) to indicate an array index. Since each of the array elements in $fields is an array, the first subscript ($fieldIndex) identifies the element (i.e., field) in the array that we want, and the second ('value') indicates that within the resulting field array we want the element with the key 'value' (i.e., the content of the field). All of this is much simpler than it sounds when you describe it, but since there’s no real equivalent in RPG it’s difficult to draw a comparison. If we suppose RPG could indeed access array elements using a key, then the equivalent RPG code might look like this: fields(fieldIndex).field('value'). How did we know that the key for the required element was 'value'? We cheated—we read the manual! But we could also have done a var_dump($fields) or print_r($fields), which would have shown us the keys and values.
zend_5250_set_input_field($bridge, 0, "CALL DSPCUST"); // Set up to call the program
// HTML for table and page heading omitted
$action = 'ENTER'; // Set up first action as Enter to call the program
// loop until all records have been read
do {
if ($action=='ENTER') {
$response = zend_5250_submit($bridge, 'ENTER'); // <=G
} else {
$response = zend_5250_submit($bridge, 'PAGEDN');
}
$fields = zend_5250_get_output_fields($response); // <=H
$fieldCount = count($fields);
$fieldIndex = firstCustNo; // Set start index for first subfile field (Customer Number in row 1 of subfile)
while ($fields[$fieldIndex]['value'] != 'F3=Exit') { // <=I
$custNo = $fields[$fieldIndex]['value'];
$custName = $fields[$fieldIndex + 1]['value'];
$city = $fields[$fieldIndex + 2]['value'];
$state = $fields[$fieldIndex + 3]['value'];
$zip = $fields[$fieldIndex + 4]['value'];
print "<tr><td>$custNo</td><td>$custName</td>
<td>$city</td><td>$state</td><td>$zip</td></tr>"; // <=J
$fieldIndex += 5;
}
$action = 'PAGEDN'; // Set action for page down
} while ($fieldCount >= fullPage); // Continue all the time there's more to come
zend_5250_disconnect($bridge); // And then disconnect <=K
Our next task is to extract the individual field values for the row so we can use PHP's print function to output them and the requisite HTML tags. We could’ve done the extraction and printing in one operation but the syntax is a little ugly and we wanted to keep the example as simple as possible. Once the row has been output (J), we then increment the field index by 5 (there are five fields per row) to begin the next row and loop back to the “while,” which will test for the “F3=...” termination condition.
Once all rows on the page have been processed and we’ve exited the while loop, we set the action code so when we next submit a response to the 5250 program, it’ll be in the form of a Page Down key. This is immediately followed by the “while...” test that controls the do loop. It checks to see if the page we’ve just processed was full (i.e., had a full 16 subfile rows) and will cause the loop to exit if anything other than a full page was displayed. When we exit the loop, we simply close the connection (K) and that’s that.
The program as it stands is by no means perfect—for example, it doesn’t attempt to handle any error conditions that might arise—but we think it demonstrates quite nicely the kind of additional capabilities the Bridge can bring to your 5250 applications. In future articles, we hope to introduce you to some of the other capabilities of the Bridge. Zend is also due to make some additional announcements about future capabilities in the Bridge product this month (October) so we’re looking forward to seeing what the future may hold in store for this interesting product.
Browse products and services for Developer.