Zend PHP Toolkit

(click to open)

Quick Page Table of Contents


Zend PHP Toolkit

Goto Main Page
Goto Documents

This page is under construction

New PHP Toolkit (ToolkitService)

     |              Browser                  |
     | Download RPG (1) | Download PHP (2)   |
     | 1) XMLSERVICE    | a) PHP CW Toolkit  |<-(1) cw-tk-php-x.x.x.zip
     |    HTML/XML/REST | b) New PHP Toolkit |<-(2) cw-tk-php-x.x.x.zip 
     |    no PHP        |--------------------|
     |    (xmlcgi.pgm)  | c) PHP “Raw XML”   |
     |    (optional)    |   (ibm_db2, odbc)  |
     |                  |--------------------|
     | 2) XMLSERVICE DB2 stored procedures   |
     |    (iPLUG4K, iPLUG32K, ..., iPLUG15M) |
     | 3) XMLSERVICE (xmlservice.pgm)        |
     |    call most anything on IBM i ...    |
     |    (PGM, SRVPGM, PASE, DB2, etc.)     |

Zend Server for IBM i 5.6.0+ includes two new features:

  • (1) PHP old toolkit compatibility layer (CW)
  • (2) New PHP Toolkit (ToolkitService)

This page will cover fundamentals of both PHP interfaces.

Zend Server PHP toolkit can be used from both IBM i (1-tier) and remote to IBM i from Linux, Windows and Mac (2-tier).

Under the covers, PHP toolkit uses the stored procedure interface to XMLSERVICE for safe, reliable transport of the XML input/output documents. Toolkit support can be used with ODBC drivers and DB2 Connect drivers from Windows/Linux or IBM i native default PASE drivers. Most people find that ibm_db2 is the best interface to use:

  • IBM i native PHP+ibm_db2 native default PASE drivers (libdb400.a)
    • all included with Zend Server (plus current Zend PTF)
  • Linux/Windows PHP+ibm_db2 DB2 Connect (DRDA port 446)
    • you will have to copy toolkit PHP modules to your PC manually, just copy this directory from IBM i to your PC /usr/local/zendsvr/share/ToolkitAPI

Step 1) Installation

Installation of XMLSERVICE and the PHP toolkit is automatic with Zend Server 5.1.1 and beyond. All configuration is completed with the installation, but you may view the XMLSERVICE documentation from the main page if you want to understand the “plumbing” of this support.

Optional: XMLSERVICE download includes alternate CRTXML2 CL program that updates Zend production library ZZENDSVR RPG - compile into ZENDSVR (see installation).

Step 2) Configuration

IMPORTANT: If you are running a machine with CCSID 65535 (and nothing works), please read and follow the documentation (main XMLSERVICE page), setting valid CCSID like 37 for Apache (web) and/or command line PHP (pear tests).

/www/zendsvr/conf/httpd.conf (web admin GUI port 2001 - ZENDSVR):
DefaultFsCCSID 37

Step 3) Example PGM call

Example calling a typical RPG PGM with a few parameters, one of which is a data structure. In this case all the parameters are io=‘both’ (input/output), so the XML going in looks exactly like the XML coming out of XMLSERVICE, except the return XML data values will change to whatever the called function writes into the parameter variables.

Please note that parameter data types being passed are packed decimal (7p4, 12p2), both as individual parameters and within data structures, not just character (1a). In fact, XMLSERVICE essentially acts much like a mini RPG compilier lining up the correctly converted XML string data to it’s actual program call geometery, so that if you debug your called program all you see is real packed decimals in variables/memory (or zone decimal 12s2, or float 4f/8f, or integer 10i0/5i0, or hex binary 4000b, etc.).


//The ToolkitService connection method/function uses either IBM_DB2(default)or ODBC extensions to connect
//to IBM i server. In order to switch to ODBC connection assign an "odbc' value to the $extension varibale
//and make sure that the ODBC extension is enabled in the PHP.INI file.
//The ODBC extension usage in ToolkitService is preferable in 2 tier environment: Zend Server running in Windows/Linux
//and accessing database and/or programs in IBM i server


try { $ToolkitServiceObj = ToolkitService::getInstance($db, $user, $pass, $extension); }
catch (Exception $e) { die($e->getMessage()); }

array('InternalKey'=>$internalKey, // route to same XMLSERVICE job /tmp/myjob1
'subsystem'=>"QGPL/QDFTJOBD",      // subsystem/jobd to start XMLSERVICE (if not running) 
'plug'=>"iPLUG32K"));              // max size data i/o (iPLUG4K,32K,65K,512K,1M,5M,10M,15M)

//     D  INCHARA        S              1a
//     D  INCHARB        S              1a
//     D  INDEC1         S              7p 4        
//     D  INDEC2         S             12p 2
//     D  INDS1          DS                  
//     D   DSCHARA                      1a
//     D   DSCHARB                      1a           
//     D   DSDEC1                       7p 4      
//     D   DSDEC2                      12p 2            
//      *+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//      * main(): Control flow
//      *+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//     C     *Entry        PLIST                   
//     C                   PARM                    INCHARA
//     C                   PARM                    INCHARB
//     C                   PARM                    INDEC1
//     C                   PARM                    INDEC2
//     C                   PARM                    INDS1
$param[] = $ToolkitServiceObj->AddParameterChar   ('both',  1,  'INCHARA', 'var1', 'Y');
$param[] = $ToolkitServiceObj->AddParameterChar   ('both',  1,  'INCHARB', 'var2', 'Z');
$param[] = $ToolkitServiceObj->AddParameterPackDec('both',  7,4,'INDEC1',  'var3', '001.0001');
$param[] = $ToolkitServiceObj->AddParameterPackDec('both', 12,2,'INDEC2',  'var4', '0000000003.04');
   $ds[] = $ToolkitServiceObj->AddParameterChar   ('both',  1,  'DSCHARA', 'ds1',  'A');
   $ds[] = $ToolkitServiceObj->AddParameterChar   ('both',  1,  'DSCHARB', 'ds2',  'B');
   $ds[] = $ToolkitServiceObj->AddParameterPackDec('both',  7,4,'DSDEC1',  'ds3',  '005.0007');
   $ds[] = $ToolkitServiceObj->AddParameterPackDec('both', 12,2,'DSDEC1',  'ds4',  '0000000006.08');
$param[] = $ToolkitServiceObj->AddDataStruct($ds);
$result  = $ToolkitServiceObj->PgmCall('ZZCALL', $libxmlservice, $param, null, null);

// simple dump output

// Do not use the disconnect() function for "state full" connection

New Toolkit stateless or state full?

The setToolkitServiceParams ‘InternalKey’=>”/tmp/packers” controls XMLSERVICE processing to run stateless in-process or state full separate process.

1) Stateless (some SRVPGM programs)

   // CLIENT             SERVER (job1)
   // ----------------||---------------------------------------------
   // client1(sally)->||->QSQSRVR1(sally)
   //                 ||     +
   //                 ||  XMLSERVICE (sally) <--shut down after each request
   // client2(sally)->||->QSQSRVR2(sally)
   //                 ||     +
   //                 ||  XMLSERVICE (sally) <--shut down after each request

2) State full (most RPG programs)
     array('InternalKey'=>"/tmp/packers", // route to same XMLSERVICE job /tmp/packers
           'subsystem'=>"QGPL/QDFTJOBD",  // subsystem/jobd to start XMLSERVICE (if not running) 
           'plug'=>"iPLUG10M"));          // max size data i/o (iPLUG4K,32K,65K,512K,1M,5M,10M,15M)

   // CLIENT             SERVER (job1)       SERVER (job2)
   // ----------------||-------------------||------------------------
   // client1(sally)->||->QSQSRVR1(sally)->||->XMLSERVICE (sally)<-|  <--alive until stopped (or idle timemout)
   // client2(sally)->||->QSQSRVR2(sally)--||----------------------|
   // client3(sally)->||->QSQSRVR3(sally)--||----------------------|
  • Stateless: If you exclude array’InternalKey’=>$internalKey, you will run in the calling process DB2 connection (QSQSRVR job). When XMLSERVICE completes your XML script it will shut down to nothing, considered stateless and holds zero state on return.
    • In general you will run slower in stateless mode (CW default / Toolkit default), because XMLSERVICE has to keep starting things over and over and over again, but perhaps not an issue if you have CPU to burn.
    • The is no semaphore locking or shared memory ipc when running as stateless (*here), because only one sally client/server is a pair, but of course there may be many sally client/server pairs on the same machine.
  • State full: If you choose ‘InternalKey’=>”/tmp/packers”, you will run in a separate job past the calling DB2 connection (child job of QSQSRVR). This will allow you to return to the same XMLSERVICE job from any connection to the machine, therefore considered “state full” and any called program can keep open files, transactions, etc. (just like a real RPG 5250 program does mate).
    • ‘InternalKey’=>’/tmp/anything’ can be any unique/accessible directory you want to route you back to same XMLSERVICE job, but usually anchored in /tmp directory because xmlservice will try to create it if missing.
    • Technically ‘InternalKey’=>”/tmp/packers” is a unique IFS machine location in posix function ftok(‘/tmp/packers’) which presents a unique numeric key representing /tmp/packers that is used for XMLSERVICE shared memory and semaphores creation/attach (XMLSERVICE uses shared memory/semaphores for communication).
    • Shared memory + semaphore locking is only required for state full connections (‘InternalKey’=>”/tmp/packers”), where each sally XMLSERVICE semaphore “front door lock” will allow only one sally client to chat with a XMLSERVICE job, the other sally requesters will wait until they are invited to chat (just like the dentist office).
    • Security is managed through IFS shared memory / semaphores access control just like any other IFS file, so once profile sally owns an active XMLSERVICE ctl+ipc then no other profile can attach to the active XMLSERVICE job … well … except for high authority profiles like *SECOFR (of course).
    • With version 1.6.6 state full XMLSERVICE connections are ended via configurable idle timeout, you may keep the jobs alive to match the original version behavior. There are other options for client wait and waiting for called program to return.
    • In this example we have been using one sally client/server ‘InternalKey’=>”/tmp/packers”, you can of course have many different sally client/server (‘InternalKey’=>”/tmp/packers”, ‘InternalKey’=>”/tmp/vikings”, ‘InternalKey’=>”/tmp/bears”, etc.) and each of these sally ipcs may have many clients chatting with each sally server ipc … sort of a sally work load balancing dream situation where we can clone a new sally ipc server for each task at hand.

Need new PHP Toolkit debug help?

We hope all our scripts just run 1st time, but most often it takes a few iterations to get things to work. PHP Toolkit offers a debug log to see XML input/output XMLSERVICE. To write to the debug log, set debug=true in toolkit.ini (/usr/local/zendsvr/share/toolkitapi/toolkit.ini). By default the log will be located in /usr/local/zendsvr/share/toolkitapi/debug.log. You can also control debugging programmatically with: $conn→setToolkitServiceParams(array(‘debug’=>true));

'plug' => "iPLUG32K"));

How to make your toolkit jobs time out

XMLSERVICE from 1.6.2 onward contains flexible timeout features, including the ability to have a program (RPG, COBOL, CL, whatever) time out if it runs too long. This is useful to end jobs that get hung with a MSGWAIT condition.

The PHP toolkit wrappers currently enable only one of these timeout features: the “idle timeout.” When using a private connection job, also known as jobs that have an IPC or InternalKey, the job can be made to time out when a certain number of seconds of inactivity have elapsed.

Cleanliness vs. performance Causing jobs to time out quickly will give you a nice, empty, clean-looking ZENDSVR subsystem, but will drag down performance the next time the job is started up. Try to find a balance between cleanliness and performance. If you plan to use the same jobs over and over, you may wish to NOT time out the jobs, either never ending them or ending them *IMMED at night, or some other scheme.

How to choose a timeout value If you have many transient users who will briefly use the site and then not return, you may want a quick timeout (30 seconds?). For users who will return over and over again, you may want a long or nonexistent timeout.

  • Idle timeout with the new toolkit API

The “idle timeout” can be set or changed any time in this way:

// let's assume the toolkit connection has been established using getInstance() and is present in the variable $conn:
$idleTimeoutSeconds = 1800; // time out job after 1800 seconds (30 minutes) of inactivity
                            // a value of 0 means no timeout (infinite wait)
$conn->setToolkitServiceParams(array('idleTimeout' => $idleTimeoutSeconds));
  • Idle timeout with the Compatibility Wrapper (CW)

Just as with the old toolkit, when you connect with i5_pconnect(), you can set the timeout interval and the number of seconds to wait. As with the old toolkit, idle timeouts only work with private connections. Use the constant I5_OPTIONS_IDLE_TIMEOUT to provide the number of seconds. Use zero (0) seconds to never time out (the default).

$privateNum = 0; // private conn number of zero causes the CW to generate a number for next time.
$idleTimeoutSeconds = 1800; // time out job after 1800 seconds (30 minutes) of inactivity
                            // a value of 0 means no timeout (infinite wait)
$options = array(I5_OPTIONS_PRIVATE_CONNECTION => $privateNum,
                 I5_OPTIONS_IDLE_TIMEOUT       => $idleTimeoutSeconds

// connect (note: the "p" for persistent is required for CW private connections) 
// and specify a private connection and timeout
$conn = i5_pconnect('localhost', 'user', 'pw', $options);

Note: The CW also supports the new API’s “setToolkitServiceParams” technique described above, because the CW uses the new toolkit underneath.

PHP old toolkit compatibility layer (CW)

     |              Browser                  |
     | Download RPG (1) | Download PHP (2)   |
     | 1) XMLSERVICE    | a) PHP CW Toolkit  |<-(1) cw-tk-php-x.x.x.zip
     |    HTML/XML/REST | b) New PHP Toolkit |<-(2) cw-tk-php-x.x.x.zip 
     |    no PHP        |--------------------|
     |    (xmlcgi.pgm)  | c) PHP “Raw XML”   |
     |    (optional)    |   (ibm_db2, odbc)  |
     |    -----------------------------------|
     | 2) XMLSERVICE DB2 stored procedures   |
     |    (iPLUG4K, iPLUG32K, ..., iPLUG15M) |
     | 3) XMLSERVICE (xmlservice.pgm)        |
     |    call most anything on IBM i ...    |
     |    (PGM, SRVPGM, PASE, DB2, etc.)     |

(2) For current Zend Server customers using old i5 toolkit … a compatibility layer was created … and so on …

Toolkit Compatibility Wrapper and Zend Toolkit API for Zend Server Beta 1.2.3

Change log link: CWChangeLog

Note: this release is designed to work with XMLSERVICE v1.6.6 (see the change log in the following link XMLSERVICE)

Important PHP configuration note

Make sure you’ve set the ToolkitAPI directory path in PHP.INI file:

include_path = ".:/usr/local/zendsvr/share/ZendFramework/library:/usr/local/zendsvr/share/pear:/usr/local/zendsvr/share/ToolkitApi"

Important Apache configuration note

Make sure you’ve set a CCSID in Apache. If you don’t, the toolkit may not work properly.

Required PTFs for XML Toolkit
Version License Program PTF
V5R4 5722SS1 SI39610
6.1 5761SS1 SI39829
7.1 5770SS1 SI39831/SI39917


Configuration of PHP and Apache to run this beta Because CW uses the same function names as the i5 Toolkit extension, the extension must be disabled in order to test CW.

  • Scenario 1: A fresh installation of Zend Server 5.6 or higher

In this case, there’s no i5 Toolkit extension loaded, so there’s no extension to disable in order to use the CW. Add this line at the top of your PHP script:


Also, for any program call or command or userspace “get” that creates variables, add this line after each call:

if (function_exists('i5_output')) extract(i5_output());

In addition, scroll down to “Changes in compatibility” in this readme for more information on possible changes in CW compared to the old i5 Toolkit extension.

  • Scenario 2: A PTF installation of Zend Server 5.6 or higher, or a previous version.

In this case, the old i5 Toolkit extension may be loaded. To test CW, you’ll have to disable that extension somehow.

  • Method a:

Load Zend Server 5.6 (or higher) in a separate LPAR (partition). Then follow instructions under Scenario 1 above.

  • Method b:

Test in your standard ZENDSVR Apache instance. This way is easiest, but any production applications that rely on i5 Toolkit may not work correctly if the extension is disabled. Disable i5 Toolkit.

1. Comment out the following directive in the /usr/local/zendsvr/etc/php.ini file
a. ;extension=i5comm.so
2. Restart Apache (through Zend Server GUI “Restart PHP” button, or on a command line,
Run test script
1. Http://i_server:10088/CW/cwtest.php
  • Method c:

Create a second Apache instance called CW where you can disable the i5comm extension. This “second instance” method is not supported by Zend, but allows you to test CW without interfering (we hope) with existing applications. You’ll also get “auto prepending” of CW in your tests so you won’t even have to add an “include” or “require.” Copy the contents of /usr/local/ZendSvr/etc to a new folder called /usr/local/ZendSvr/etccw.

Edit /usr/local/ZendSvr/etccw/php.ini to comment out the i5comm.so extension:
; disable i5 toolkit extension

Also in this special PHP.INI file, add ToolkitAPI path to the include_path directive:


In the special PHP.INI file, you may want to set max_execution_time = 120 or so, so that experimental long-running demo scripts will not end prematurely. Find “auto_prepend_file” in the INI and set it as below to point to the shared location (within the include_path set earlier) of the CW library, so all your scripts in this instance will automatically use CW:

auto_prepend_file = "CW/cw.php"

Save this INI file.

Now, set up a new Apache instance, called CW, with configuration in /www/cw/conf and its own fastcgi.conf in the same folder. The CW instance will have a different port number than usual, perhaps something like 10092. in /www/cw/conf/fastcgi.conf, point to the custom directory where php.ini resides:

If you need to change anything in conf.d, you can also set:

Start the CW Apache instance. If you make any further changes to PHP.INI or conf.d, restart Apache. Run test script (assuming CW runs on port 10092…you can choose your own IP and port, of course)


If you want to run your existing scripts with this CW server, you can let CW see them by adding Alias statements in Apache.

# Allow CW instance to run old toolkit class library from zendsvr
Alias /i5Toolkit_library /www/zendsvr/htdocs/i5Toolkit_library
# Allow requests for files in zend server folders (see aliases
<Directory /www/zendsvr/htdocs>
Options FollowSymLinks
order allow,deny
allow from all
AllowOverride all
Configuration of the CW software
You’ll find a config file called /usr/local/zendsvr/share/ToolkitApi/toolkit.ini. 

Options include:
debug=true or false. If debugging is on, then XML input , output and more will be
written to a debug file (default: /usr/local/zendsvr/share/ToolkitApi/debug.log, but see
next bullet point).
debugLogFile = "/usr/local/zendsvr/share/ToolkitApi/debug.log" sets the filename of
the debug log, which is used when debug = true (see previous bullet point)
Logfile=path Establishes a separate toolkit log file where warnings and errors will be
logged. Log your own messages using $conn->logThis(‘string to log’);
Under the [hosts] section you can set up mappings to custom database names, if any.
Generic local database names have been set up by default.
Encoding= value such as "ISO-8859-1"
sbmjob_params = "ZENDSVR/ZSVR_JOBD/XTOOLKIT" or other optional JOBDLIB/JOBD/JOBNAME

Under the [demo] section, various settings to try features in the CW demo Changes in compatibility

IMPORTANT: Change to scripts possibly required Calls to i5_program_call(), i5_userspace_get(), and i5_command() can provide output by creating PHP variables. The i5 Toolkit seemingly exported these variables into local scope (outside the i5_ function, of course), while the CW beta exports such variables into global scope. Therefore, if these functions are used within a class or a function call, please add the line:

if (function_exists('i5_output')) extract(i5_output());

after the function call.

If, however, i5_program_call(), i5_userspace_get(), and i5_command() are used in global space, (directly in the called script, not from inside a function), no changes are required.

  • Many i5 Toolkit functions, such as i5_connect() and i5_program_prepare(), returned
   “Resource” objects. CW will instead return native PHP objects. Any code that checks for
      is_resource() should use is_object() instead, or simply check that the object is !== false.
  • I5_OPTIONS_PRIVATE_CONNECTION: No error will be returned if a nonexistent private
   connection number is passed in. Unlike the old toolkit, XMLSERVICE will create the
  • The new toolkit is more strict about upper/lower-case than the old toolkit.
Example: If your program description array includes a parameter named "edtVar", and your input array provides a value for it, the input array must also specify "edtVar", not "edtvar" or "EDTVAR".
  • If you receive the error “PCDATA invalid Char value,” most likely your program

call is returning binary data, which was unusable and ignored in the old toolkit, but which the SimpleXML parser cannot parse. An error message will show you which field/parameter on the program call has the problem. You can eliminate the error by changing your CHAR field to type BYTE (binary). e.g. original: array(“Type”=>I5_TYPE_CHAR, “Name”=>”PWDDate”,”Length”=>’8′, “IO”=>I5_OUT) e.g. improved: array(“Type”=>I5_TYPE_BYTE, “Name”=>”PWDDate”,”Length”=>’8′, “IO”=>I5_OUT)

Limitations of this beta

  • Although XMLSERVICE and Zend’s new PHP wrapper can work over transports other

than ibm_db2, such as ODBC and CGI, allowing great flexibility, the CW has not yet been tested rigorously with these. For this beta, the focus is on ibm_db2, the best performing transport.

  • Record level access (i5_open, i5_data_seek) is not included.
  • SQL access (i5_query, etc.) is not currently supported. It may be supported in a future


If an error occurs, some functions provide CPF messages, some provide CPF plus error text, while others provide a more generic message. In particular, non-English systems may not report error text correctly. Error reporting will be improved in future releases, if possible.

The i5_joblog_list() function works but returns a limited set of data for each joblog. This may be enhanced in future. For users of 5250 Bridge

Zend Server 5.1.1 (only available from IBM at this time) and Zend Server 5.6.0 include an updated version of 5250 Bridge that works with the new toolkit, so the CW is not required for the 5250 Bridge. If you encounter a problem during the beta period, it could indicate a problem for everyone, so please post the issue on the forum as soon as possible.

Toolkit Manuals

  • Zend Manual - reference material for new PHP Toolkit
  • CW Layer - Toolkit Compatibility Wrapper(Beta)
  • Easycom For PHP - Easycom for PHP is formally known as the PHP Toolkit For IBM i, or i5 Toolkit