Skip to the content.

Purpose

I have created this guide as a reference for myself, but also to help others who might be trying to accomplish the same thing. Much of this info is copied and pasted, and I do not represent it as my own, only that I have aggregated it into one place for ease-of-use (there were many different sources for all of this).

Notably, thank you to Chuk Shirley, Stephanie Rabbini, Alan Seiden, Dave Dressler, Dawn May, and Kevin Adler.

Synopsis (What The Heck Does This All Mean??)

IBM i OS on IBM Power Systems hardware is an ABSOLUTELY amazing collection of technologies with true rock solid stability and features to help with enterprise level challenges. However, the perception of the platform (Usually referred to as AS/400 or iSeries) is that the technology is dated and not suited for the modern business environment.

This is 100% innaccurate and not true, and mostly comes from the command line interface users would be expected to use (IBM 5250) and/or some of the programming quirks that arise from being able to compile and run all code since its inception.

Instead there are options to leverage the IBM i OS to its absolute strength while employing modern techniques / interfaces so that users.

The power of IBM RPG is that it is write once, compile, and run on the platform ‘forever’. No matter the OS upgrades and new hardware any of your code written in RPG / CL will run on the new system. If used correctly this can lower your future technical debt and allow a lot more

For your consideration I would suggest following (or putting into place with your current applications) the CQRS design philosphy to your application. We (K3S) have modernized our application by taking every action done within a database that is not a query (the COMMAND action within CQRS, which usually represents a Create, Update, or Delete of a CRUD application) and created single purpose APIs to accomplish a task called with the IBM i PHP Toolkit. For example: if within our application we wanted to create a new supplier, we would call the ADDSUPL API. This API will take in the values needed to create a supplier, and if all the values are valid (meaning there can be a lot of business logic that is encapsulated within the API) it will be successful, or fail and return a valid message. This means these business logic APIs, once compiled and working, will be viable for a long time.

For the Query portion of CQRS you can then use whatever preferred interface language you want. Below we have used PHP to accomplish this action. It has allowed us to use a modern PHP framework called Laminas (A community-supported, open source continuation of Zend Framework), modern styling via Bootstrap, and the flexibility that if we want to change our interface to a different language (though we love PHP!).

This guide was written with these ideas and goals in mind and we hope others use them to gain the full potential of IBM i!

Below I have written out instructions for 4 distinct scenarios:

I have also included:

Installing PHP RPM on IBM i

This is a guide on how to install the PHP RPMs from Zend. This is the community edition you can use as any normal open source software. The ODBC extension is not installed with the normal Zend Server version of PHP, so if you want to use ODBC with PHP you are going to need to install the RPMs. Zend Server and PHP RPMs are pragmatically no different from a run time perspective. Most of the advantage in Zend Server is in the development, going through logging, and they have a custom package management. As well the intl and zip extensions are not available via the RPMs. Otherwise most shops should just consider the PHP RPMs.

  1. Setup Package Manager: Make sure the you have installed the Open Source Package Management (OSPM) from ACS Getting started with Open Source Package Management in IBM i ACS

If you do not have SSH working to setup OSPM, these instructions will allow you to install OSPM from ACS

  1. Install yum utilities: From the OSPM install yum-utils from the Available Packages tab. This will allow you to add 3rd party packages.

  2. Install PHP RPMs: From a shell command line (I recommend SSHing in to the server, but I believe you can use QSH) install the repo to PHP RPMs hosted by Zend. Here is a list of 3rd Party RPMs (make sure you read the note below before you add PHP). All of the open source software shoudl be in the ‘/QOpenSys’ directory and yum specifically in ‘/QOpenSys/pkgs/bin’.

    3rd Party Open Source Repos for IBM i

    yum-config-manager --add-repo http://repos.zend.com/ibmiphp/
    

    The command I ran because of where yum is located

    /QOpenSys/pkgs/bin/yum-config-manager --add-repo http://repos.zend.com/ibmiphp/
    

    As the repo RPMs for PHP were now added to ACS, now, just as you added yum-utils from the Available Packages tab, add the PHP packages / extensions you want. I would just add all of them that begin with php. They are not very large and you will end up probably using all of them.

  3. Configuring PHP: Mostly you will use the defaults already setup, but you can configure PHP now to fit your environment. You will need to move the php.ini file into the php sub directory to use the .ini file.

    The default php.ini file is located in this directory:

    /QOpenSys/etc/php.ini
    

    This must be moved to the php subdirectory to be loaded by PHP RPMs:

    /QOpenSys/etc/php/php.ini
    

    Extensions are enabled via this directory:

    /QOpenSys/etc/php/conf.d
    

    Check that your php.ini file is loaded by creating an index.php file in your default htdocs directory with the below configuration output:

    <?php phpinfo(); ?>
    

    Visit PHP.net to learn about configuration options.

    Default versions of configuration files and all the extensions are included when you install the RPMs.

    Updates to RPMs: If you have made changes to the configuration files, subsequent RPM updates will preserve your changes. If you are running the config files unchanged from install it will install the newer versions. But if you made changes to the config files it will keep the versions you have and write the new default ones to the same directory with this naming scheme: filename.rpmnew

Using chroot to install multiple versions of PHP

IBM i chroot is an open source package that allows you to create an IFS level container for different open source packages. Contextually chroot ‘changes the root’ for certain operations so to the instance this sub-directory looks and acts like the root directory. This allows us to install multiple versions / instances of things (like the open source applications via yum) which were only designed to be installed once per machine.

With these multiple chroot environments we could have an install of PHP 7.4, 8.0, 8.1, and 8.2 running on the same machine, but maintained independently.

To install chroot:

yum install ibmichroot

Once installed lets create a CHROOT for housing the standard version of PHP released by Zend, 7.3. Note below I have named the chroot PHP73, but you can choose to name it whatever you would like.

chroot_setup -y /QOpenSys/chroots/PHP73

Setup Apache to Run PHP

  1. Create New Apache Instance On IBM i: Visit http://ibmiipaddress:2001/HTTPAdmin on your server where you replace ibmiipaddress with the IP address of your IBM server (note that the Admin Server must be running). Click ‘Create HTTP Server’ in the top left hand corner, and go through the steps of adding a new Apache server. Note the webroot folders if you adjust from defaults.

  2. Set Up Aache To Run PHP:

    Once you have created the Apache instance, use the “Edit Configuration File” option on the left panel to add the following configuration options near the top:

    # This loads the Apache FastCGI support originally created for Zend
    LoadModule zend_enabler_module /QSYS.LIB/QHTTPSVR.LIB/QZFAST.SRVPGM
    
    # This tells Apache that any file with a .php extension should be
    # executed by the FastCGI application/x-httpd-php handler
    AddType application/x-httpd-php .php
    AddHandler fastcgi-script .php
    
    # Let's you go to http://example.com instead of http://example.com/index.php
    DirectoryIndex index.php index.html
    

    Once you have added these configuration options, click the OK button at the bottom of the page to save it.

  3. Configure FastCGI: Now that Apache is set up for FastCGI, we need to configure a FastCGI handler for application/x-httpd-php. Without this, the FastCGI processor won’t work and the PHP script will merely be downloaded by the web browser. As well we want to set the number of PHP jobs that can run scripts for us. Each job can handle 1 at a time. By default we will start with 10, but this can be increased if needed.

    Create a file called fastcgi.conf in /www/<server name>/conf (assuming the default path for the webroot was chosen) with the following contents:

    Server type="application/x-httpd-php" CommandLine="/QOpenSys/pkgs/bin/php-cgi" StartProcesses="1" SetEnv="PHP_FCGI_CHILDREN=10"
    

    You can now start the web server.

  4. Test: Create a small index.php file in your webroot with the following code:

    <?php phpinfo(); ?>
    

    And visit the virtual host you set up. You should see the PHP info page. If you do, you are running PHP via RPM on your IBM i.

  5. Recommended Additional Configuration Options For Apache, Speed, and a Common Issue: While your mileage may vary, I recommend these additional lines for your consideration in your Apache config to speed up your app.

    # These lines will add gzip compression to the data served. You want these near the top
    LoadModule deflate_module /QSYS.LIB/QHTTPSVR.LIB/QZSRCORE.SRVPGM
    AddOutputFilterByType DEFLATE application/x-httpd-php application/json text/css application/x-javascript application/javascript text/html
    
    # This will turn on Keep Alive and allow users to reuse older connections, making the serving of data faster. 
    TimeOut 30000
    KeepAliveTimeout 30
    HotBackup Off
    ThreadsPerChild 40
    
    # If your code looks funky a lot of times it is your CCSID. This seems to help
    DefaultFsCCSID 37
    CGIJobCCSID 37
    

Setup NGINX To Run PHP

Notes on NGINX and PHP-FPM

NGINX has built-in support for proxying requests to a FastCGI process, but it does not provide a built-in FastCGI process manager. For that, we will be using the standard PHP-FPM utility.

NGINX has a good document on setting up WordPress in NGINX. Since WordPress is written in PHP, this can be used as the basis for setting up PHP with NGINX on IBM i.

Setting up NGINX

Install NGINX from the Open Source Package Manager.

We will then need to creat an NGINX configuration. As there is no wizard this will need to be done by hand. Create a configuration under /QOpenSys/etc/nginx/ called php.conf

In this example, we will mimic the file structure of an Apache webserver on IBM i:

error_log  /www/php/logs/nginx.err.log;
pid        /www/php/logs/nginx.pid;

events {
    # required section, just leave empty for defaults
}

http {
    upstream php {
        # this is the default FPM listen address
        server 127.0.0.1:9000;
    }

    server {
        # set your actual hostname here
        server_name mywebserver.example.com;

        # set your listen port and address here
        listen 6090 default_server;

        # document root for web server
        root /www/php/htdocs;

        # Let's you go to http://example.com instead of http://example.com/index.php
        index index.php index.html;

        # If you want this to be case insensitive, you will add an * after the ~
        # Example: location ~* \.php$ {
        location ~ \.php$ {
            include /QOpenSys/etc/nginx/snippets/fastcgi-php.conf;

            # proxy to the php upstream we defined earlier
            fastcgi_pass php;
        }
    }
}

We also need to create the PHP FastCGI snippet referenced above:

fastcgi_split_path_info ^(.+\.php)(/.+)$;

# Check that the PHP script exists before passing it
try_files $fastcgi_script_name =404;

# Bypass the fact that try_files resets $fastcgi_path_info
# see: http://trac.nginx.org/nginx/ticket/321
set $path_info $fastcgi_path_info;
fastcgi_param PATH_INFO $path_info;

fastcgi_index index.php;
include fastcgi.conf;

Setting up FPM

Now that NGINX is configured, we need to set up FPM. Luckily, FPM is mostly configured ok out of the box.

The main FPM config file is located at /QOpenSys/etc/php/php-fpm.conf, which really just serves as a way to load /QOpenSys/etc/php/php-fpm.d/www.conf.

Again, the defaults should suffice, but one thing that is tricky is that FPM wants to set a user and group to run under. The default user has been set to QTMHHTTP, but this user is not a member of any groups, which FPM detects as an error:

[19-Jul-2019 13:27:35] ERROR: [pool www] please specify user and group other than root
[19-Jul-2019 13:27:35] ERROR: FPM initialization failed

There are two options:

  1. Modify QTMHHTTP user to have a primary group, eg. CHGUSRPRF USRPRF(QTMHHTTP) GRPPRF(GRP1)
  2. Configure FPM in /QOpenSys/etc/php/php-fpm.d/www.conf to run under a given user profile, eg. group = grp1

Starting Things Up

Once everything is configured, you can start everything:

Your NGINX server will be running under your user profile and FPM will be running under the user profile specified in the config.

Testing the Setup

Finally, we need a PHP script to run. Create an index.php in the document root for the web server, eg. /www/php/htdocs:

<?php echo phpinfo(); ?>

Tuning PHP and Apache

Getting Apache and PHP just to work is one thing. Being able to handle a web application under load without stressing your machine is another thing. After you have your web app up and running and ready for production, I recommend the various articles below to read about how to fine tune your machine to handle significant load from users.

Guru Right Size Your PHP OPTIMIZE YOUR IBM i WEB APPLICATION USING FASTCGI

Automatically Configuring IBM i Apache

You can install and use the tool below to do all the steps listed.

AUTOMATICALLY CONFIGURE IBM i APACHE WEB SERVER TO RUN PHP WITH SITEADD

Example ODBC Connection To DB2 On IBM i

These are some example connection strings and directions to help people connect via PDO / ODBC to DB2 on IBM i. These examples include running a PHP application on a Linux / Windows server OR running PHP directly on IBM i.

What is PDO and ODBC?

PDO (PHP Database Object) is an abstracted database connection developed for PHP. By using PDO you can write one generalized query that can ‘run anywhere’ once connected.

ODBC is a standard database connection method developed to allow applications to connect the ‘same way’ to any database.

By using PDO and ODBC to connect to DB2 on IBM i you can use generalized methods to develop your application.

IBM Connection String Reference

This is the collection of options to help you buid your ODBC string.

IBM ODBC Connection String Keywords

Important PHP Setting

It is important to set in the php.ini OR the .user.ini file this ODBC setting:

odbc.default_cursortype=0

This sets the cursor type to Forward Only Cursor, and can provide a huge speed improvement for large amounts of data.

ODBC PHP Configuration

Connecting A PHP Application Running On IBM i To A DB2 Database Running On IBM i

This is an example of how to use PDO and ODBC to connect to DB2 on IBM i when PHP is running on IBM i. This will NOT work by default with Zend Server PHP as they do not include the necessary ODBC extension. You must either add the extension or run and install the PHP RPMs listed above. From a runtime perspective using ODBC there is no difference between the two. Zend Server has some nice debugging tools and a set way to deploy applications, and some extensions are not available (intl and zip) via the RPMs. Otherwise most shops should consider just running the PHP RPMs.

I have found the order of the install of the next two pieces matter, so install step 1, then step 2.

  1. You will need to install the unixODBC and unixODBC-devel from the OSPM. These drivers, along with the PASE IBM i ODBC driver will allow your app to connect to the DB2 database.

  2. Next, while the IBM i OS has a built in ODBC server to accept connections by default, it does not have an ODBC client driver installed by default. You will need to download the PASE IBM i ODBC driver and install.

The directions will mention setting up a DSN within odbc.ini or the user odbc.ini. You can either set up your database connections this way or configure your odbc connection via a string as shown below. This approach allows you to track your connection configuration in your git repository.

<?php
/*
 * Database connection information: https://docs.zendframework.com/zend-db/adapter/
 */
return array (
    'db' => [
        'dsn' => 'odbc:DRIVER={IBM i Access ODBC Driver};SYSTEM=ipaddress;UID=ibmiusername;PWD=ibmipassword;NAM=1;TSFT=1;DBQ=, THIS IS WHERE YOU PUT THE LIBRARY LIST THE COMMA IN FRONT SAYS NO DEFAULT LIBRARY',
        'driver' => 'Pdo',
        'platform' => 'IbmDb2',
        'platform_options' => [
            'quote_identifiers' => true,
        ],
        'driver_options' => [
            PDO::ATTR_PERSISTENT => true,
            PDO::ATTR_EMULATE_PREPARES => true,
        ],
    ],
);

Connecting A PHP Application Running On Linux / Windows To A DB2 Database Running On IBM i

As PHP can run on multiple OSes, it can be beneficial in some circumstances to run PHP on another server and use IBM i just for its DB2 database and business logic (for example, calling RPG via the PHP Toolkit).

<?php
/*
 * Database connection information: https://docs.zendframework.com/zend-db/adapter/
 */
return array (
    'db' => [
        'dsn' => 'odbc:DRIVER={IBM i Access ODBC Driver};SYSTEM=ipaddress;UID=ibmiusername;PWD=ibmipassword;NAM=1;CCSID=1208;DBQ=, THIS IS WHERE YOU PUT THE LIBRARY LIST THE COMMA IN FRONT SAYS NO DEFAULT LIBRARY',
        'driver' => 'Pdo',
        'platform' => 'IbmDb2',
        'platform_options' => [
            'quote_identifiers' => true,
        ],
        'driver_options' => [
            PDO::ATTR_PERSISTENT => true,
            PDO::ATTR_EMULATE_PREPARES => true,
        ],
    ],
);

Releasing Locks On Files Created From ODBC Connections

When accessing files via the ODBC connection there are jobs created called QZDASOINIT. These jobs can lock files and, at some point, you might need to release the locks (especially if you use the Lazy Close option to speed up response time) They appear to naturally go away after 15-30 minutes, however if you need instant release we have open sourced some code to help.

Release Locks From ODBC Connections

Calling RPG via The PHP Toolkit Over PDO ODBC When PHP Runs On Linux / Windows

It is possible to call RPG from another server over ODBC. This uses your PDO connection referenced above. Below is the code from my application running in Zend Framework. Notice on instantiation of the toolkit (the new Toolkit line) I am passing the current database connection, and the fourth parameter is ‘pdo’. This is allowing the Toolkit to use our current connection resource from the PDO object over ODBC to call RPG on the IBM i (yes this is ridiculously cool).

IBM i PHP Toolkit Repo

<?php

namespace RPG\Service\Factory;

use Zend\ServiceManager\Factory\FactoryInterface;
use Interop\Container\ContainerInterface;
use ToolkitApi\Toolkit;

class ToolkitFactory implements FactoryInterface
{

    public function __invoke(ContainerInterface $container, $requestedName, array $options = null )
    {    
            /** @var \Zend\Db\Adapter\Adapter $databaseAdapter
            $databaseAdapter = $container->get('Zend\Db\Adapter\Adapter');

            $databaseConnection = $databaseAdapter->getDriver()->getConnection()->getResource();
     
            return new Toolkit($dbConn, null, null, 'pdo');
        }
    }
}

Here is the current documentation with Toolkit Examples

Need To Re-add the IBM i Repos

The IBM i repos live here: http://public.dhe.ibm.com/software/ibmi/products/pase/rpms/repo

To re-add them if you bork something:

    yum-config-manager --add-repo http://public.dhe.ibm.com/software/ibmi/products/pase/rpms/repo

This does take having the yum-config-manager already installed. If you do not have that already installed you might want to reach out to someone at IBM for help.

Or you can use our guide to install the packages found here: How To Setup Open Source Package Manager if you don’t have SSH access

Running ODBC In A Different Subsystem

By default, ODBC runs in the QUSRWRK subsystem. This can be changed to run in a different subsystem via either the requesting IP address or by the user profile. The most useful is to route the request via user profile. This will entail four pieces to get it working pieces to get working. This is all based off of IBM’s article Creating a user-defined subsystem and Dawn May’s article Routing ODBC Requests to a User-Defined Subsystem

Create A Library For The Subsystem

CRTLIB ODBCLIB TEXT('Library to hold subsystem configuration objects for ODBC Connection')

ODBCLIB is the subsystem library name. You can name it whatever you want.

Create a class. The class defines certain performance characteristics for your subsystem including run priority, time slice, and default wait times.

CRTCLS ODBCLIB/ODBCSBS RUNPTY(20) TIMESLICE(2000) DFTWAIT(30) TEXT('Custom Subsystem Class For ODBC')

With our aforementioned ODBCLIB, ODBCSBS is the class name so we have characterisits for the subsystem.

Create The Subsystem Description Or Create A Duplicate Of QUSRWRK

New Subsystem:

CRTSBSD SBSD(ODBCLIB/ODBCSBS) POOLS((1 *BASE)) TEXT('Custom Server Subsystem For ODBC')

Name is repeated, but ODBCSBS is the subsystem name.

You could also just duplicate QUSRWORK Duplicate:

CRTDUPOBJ OBJ(QUSRWRK) FROMLIB(QSYS) OBJTYPE(*SBSD) TOLIB(ODBCLIB)

Create The Prestart Jobs for QZDASOINIT For Our ODBC Connection

This will create the prestart jobs for our ODBC connection. This will allow us to have a certain number of jobs waiting for requests to come in. Without this the ODBC jobs will still be created in QUSRWRK, even if we have the routing below.

ADDPJE SBSD(ODBCLIB/ODBCSBS) PGM(QSYS/QZDASOINIT) INLJOBS(50) THRESHOLD(4) JOB(QZDASOINIT) STRJOBS(*YES)

Add the Routing Entry to the ODBC Connection Based Off Of User Profile Using SQL (note this isnt command line, but via ACS or another SQL tool)

Below is the SQL to add the routing entry to the ODBC connection based off of user profile. This will route all ODBC requests from the user profile ODBCUSER to the subsystem MYSBS.Note that the ODBCUSER needs all the proper authority to run the ODBC jobs.

CALL QSYS2.SET_SERVER_SBS_ROUTING('ODBCUSER', 'QZDASOINIT', 'ODBCSBS')

To remove the entry, just do the same entry but set the subsystem to null.

CALL QSYS2.SET_SERVER_SBS_ROUTING('ODBCUSER', 'QZDASOINIT', NULL)

Running Apache In A Different Subsystem

These instructions are based off Dawn May’s guide for Run an HTTP Server in its own subsystem

Launch your application and test!

Go to your application and visit a page that will access the database. Then run WRKACTJOB and check that the QZDASOINIT jobs are running in your new subsystem. If successful you should see your jobs in the new subsystem.

Potential Speed Improvements

There are areas of the ODBC jobs that can have impact on the speed and response of your application depending on what you have enabled / disabled or running from a log standpoint. A couple key areas to take a look at are:

Prestart ODBC Jobs (QZDASOINIT)

Prestarting ODBC jobs can ensure there is enough jobs available and waiting when the requests come in, as well as making sure the jobs cycle through fast enough to keep things working. IBM i Database Host Server and the QZDASOINIT Prestart Jobs

Exit Programs

Exit Propgrams are a fantastic tool within the IBM i world that can attach a program to lots of individual actions within the system. As an example, a program can be called every time ODBC is used to access the database. This program can be used to check extra security or do a number of logging options. If this is forgetten and left on the ODBC connection, it can add a tremendous amount of overhead. Always double check your exit programs that they are needed and part of your application environment. Harnessing Your ODBC Users with Exit Programs

Trace TCP/IP Application (TRCTCPAPP)

You can enable tracing on a TCP/IP application to learn more about the traffic within your app. If this is left on there can be a significant amount of overhead. Trace TCP/IP Application (TRCTCPAPP)

About K3S (King III Solutions, Inc)

K3S is a software development company that specializes in inventory management and procurement solutions for the distribution industry. Their applications and solutions are developed to run on the IBM i OS (the best enterprise level OS!) and interface with any ERP application on any platform.

As well K3S open sources many of its Guides & Utilities in an effort to improve the IBM i community.