Category Archives: i5

The “gotchas” of running Zend Framework 2 on older versions of Zend Server for IBM i – ZF2 skeleton Application

Have you tried the ZF2 skeleton Application on PHP on the IBM i and have run into issues getting it to work since your on ZS 5.5, 6.x and are unable to upgrade to ZS7 at the moment? Well here’s a few gotchas and things you’ll have to change to get it work correctly:


 

Fatal error: Can’t inherit abstract function Zend\Validator\Translator\TranslatorInterface::translate() (previously declared abstract in Zend\I18n\Translator\TranslatorInterface) in library/Zend/Mvc/I18n/Translator.php on line 19

1) Comment out the Translate method in i89n\Translator\TranslatorInterface.php

//public function translate($message, $textDomain = 'default', $locale = null);
//For some reason ZF2 has 2 methods with the same name but it works in newer versions of PHP

 

Zend\ServiceManager\Exception\ServiceNotFoundException

zendframework/library/Zend/ServiceManager/ServiceManager.php:529

Zend\ServiceManager\ServiceManager::get was unable to fetch or create an instance for Zend\Db\Adapter\Adapter

2) set your config_glob_paths to the full path of the config/application.config.php. Note: the path below has to be specific to where your file is located on the IFS.

'config_glob_paths' => array(
      '/www/zendsvr/htdocs/zf2test/config/autoload/{,*.}{global,local}.php',
),
//AFter updating to ZS 7 you should be able to change this back to the relative path of config/autoload/{,*.}{global,local}.php

 

“album” in MYLIB type *FILE not found. SQLCODE=-204

3) Add ‘platform_options’ to your config/autoload/global.php

//The SQL that is generated by Zend's classes is putting quotes around the table like:
//SELECT 'album'.* FROM 'album'
//after turning off quote_identifiers it will run this SQL:
//SELECT album.* FROM album
return array(
     'db' => array(
         'driver'           => 'IbmDb2',
         'platform'         => 'IbmDb2',
         'platform_options' => array('quote_identifiers' => false),
         'database'         => '*LOCAL', // IBM i serial number or db directory entry name goes here
         'persistent' => true,
        'driver_options'    => array(
            'autocommit'     => DB2_AUTOCOMMIT_OFF, //::TODO:: We might not be able to use the constant since db2 may have not loaded at this point
            'i5_naming'     => DB2_I5_NAMING_ON, //::TODO:: We might not be able to use the constant since db2 may have not loaded at this point
            'i5_lib'        => 'MYLIB',
            'i5_libl'       => 'MYLIB MYLIB2 MYLIB3',
            ),
     ),
     'service_manager' => array(
         'factories' => array(
             'Zend\Db\Adapter\Adapter'
                     => 'Zend\Db\Adapter\AdapterServiceFactory',
         ),
     ),
 );

 

You’re view doesn’t display titles or artists

4) This is because the DB2 is case-insensitive and automatically converts lower-case column names that were created in the CREATE TABLE ALBUM script to upper-case columns (i.e. title is now TITLE and artist is now ARTIST). You can either modify the CREATE TABLE sql to force lower case column names by putting double quotes around your columns ( i.e. “title” “artist”). If you do it this way you’ll always have to put double quotes around it when using SQL, but in the PHP it won’t require double quotes. OR you can change the Album Model’s exchangeArray() to use the upper case column names. I recommend using the upper-case columns because the Zend\Db\Adapter\Driver\IbmDb2\ibmdb2 Statement class doesn’t know that it has to put double quotes around lowercase field names. The only approach with this is that you’ll have to change the field names to uppercase throughout the Getting Started Example

Solution 1

/* Creating table MYLIB.ALBUM */
DROP Table MYLIB.ALBUM;
CREATE TABLE MYLIB.ALBUM (
    'id' INTEGER GENERATED ALWAYS AS IDENTITY (START WITH 1, INCREMENT BY 1, NO ORDER, NO CYCLE, NO MINVALUE, NO MAXVALUE, CACHE 20) NOT HIDDEN ,
    'artist' VARCHAR (100) NOT NULL NOT HIDDEN ,
    'title' VARCHAR (100) NOT NULL NOT HIDDEN ,
    PRIMARY KEY ('id')
) NOT VOLATILE ;

LABEL ON TABLE MYLIB.ALBUM IS 'Test table for ZF2' ;

 INSERT INTO MYLIB.ALBUM ('artist', 'title')
     VALUES  ('The  Military  Wives',  'In  My  Dreams');
 INSERT INTO MYLIB.ALBUM ('artist', 'title')
     VALUES  ('Adele',  '21');
 INSERT INTO MYLIB.ALBUM ('artist', 'title')
     VALUES  ('Bruce  Springsteen',  'Wrecking Ball (Deluxe)');
 INSERT INTO MYLIB.ALBUM ('artist', 'title')
     VALUES  ('Lana  Del  Rey',  'Born  To  Die');
 INSERT INTO MYLIB.ALBUM ('artist', 'title')
     VALUES  ('Gotye',  'Making  Mirrors');

GRANT SELECT,INSERT,UPDATE,DELETE ON MYLIB.ALBUM TO MYUSER;
SELECT * FROM MYLIB.ALBUM

Solution 2

<?php
namespace Album\Model;

 class Album
 {
     public $id;
     public $artist;
     public $title;

     public function exchangeArray($data)
     {
     //SOLUTION 2 - change the PHP file to use the upper-case column names
         $this->id     = (!empty($data['ID'])) ? $data['ID'] : null;
         $this->artist = (!empty($data['ARTIST'])) ? $data['ARTIST'] : null;
         $this->title  = (!empty($data['TITLE'])) ? $data['TITLE'] : null;
     }

	 public function getInputFilter()
     {
         if (!$this->inputFilter) {
             $inputFilter = new InputFilter();

             $inputFilter->add(array(
                 'name'     => 'ID',
                 'required' => true,
                 'filters'  => array(
                     array('name' => 'Int'),
                 ),
             ));

             $inputFilter->add(array(
                 'name'     => 'ARTIST',
                 'required' => true,
                 'filters'  => array(
                     array('name' => 'StripTags'),
                     array('name' => 'StringTrim'),
                 ),
                 'validators' => array(
                     array(
                         'name'    => 'StringLength',
                         'options' => array(
                             'encoding' => 'UTF-8',
                             'min'      => 1,
                             'max'      => 100,
                         ),
                     ),
                 ),
             ));

             $inputFilter->add(array(
                 'name'     => 'TITLE',
                 'required' => true,
                 'filters'  => array(
                     array('name' => 'StripTags'),
                     array('name' => 'StringTrim'),
                 ),
                 'validators' => array(
                     array(
                         'name'    => 'StringLength',
                         'options' => array(
                             'encoding' => 'UTF-8',
                             'min'      => 1,
                             'max'      => 100,
                         ),
                     ),
                 ),
             ));

             $this->inputFilter = $inputFilter;
         }

         return $this->inputFilter;
     }
 }

class AlbumForm extends Form
 {
     public function __construct($name = null)
     {
         // we want to ignore the name passed
         parent::__construct('album');

         $this->add(array(
             'name' => 'ID',
             'type' => 'Hidden',
         ));
         $this->add(array(
             'name' => 'TITLE',
             'type' => 'Text',
             'options' => array(
                 'label' => 'Title',
             ),
         ));
         $this->add(array(
             'name' => 'ARTIST',
             'type' => 'Text',
             'options' => array(
                 'label' => 'Artist',
             ),
         ));
         $this->add(array(
             'name' => 'submit',
             'type' => 'Submit',
             'attributes' => array(
                 'value' => 'Go',
                 'id' => 'submitbutton',
             ),
         ));
     }
 }

Fatal error: Can’t inherit abstract function Zend\Form\LabelAwareInterface::setLabel() (previously declared abstract in Zend\Form\ElementInterface) in /www/zendsvr/htdocs/zf2test/vendor/zendframework/zendframework/library/Zend/Form/Element.php on line 21

5) Comment out setlabel and getlabel in Zend/Form/View/LabelAwareInterface.php – https://github.com/zendframework/zf2/issues/5996

//public function setLabel($label);
    //public function getLabel();

Doctrine is not supported for the IBM DB2

This isn’t a ZF2 issue directly, but if you try and use ZF2 plugins such as ZFCuser you’ll run into issues. Doctrine doesn’t work that well with IBM DB2 currently. The current implementation is based on DB2 for Linux. There is talk about creating new classes that would handle the IBM DB2 specifically but there hasn’t been any work on it. A conversation looking into solving this is being had at – https://github.com/doctrine/dbal/pull/518 . Novice PHP programmers remember that there’s multiple ways of solving a problem and doctrine is not a must have to get the job done.

UPDATE on Case-sensitivity – Dec 3 2014

I think some of the case sensitivity issues can be fixed if Zend Framework’s Zend\Db\Adapter\Driver\IbmDb2\ibmdb2 is updated to use QSYS2.DELIMIT_NAME which is available in V7R1 TR 8 and V7R2. Also doctrine could be used if they used that feature. Not everyone is on that Version of the OS so this would need to be a feature that should be explicitly set. More info on QSYS2.DELIMIT_NAME – http://www.itjungle.com/fhg/fhg111214-story01.html

Support for long passwords may break your login page on Zend Server for IBM i

To keep up with security on the IBM i, your company will eventual support long passwords and more characters.  This means people can use symbols and lower case characters which will help increase the difficulty of guessing a password.  The problem you may run into with your PHP script is if you were using the strtoupper() function on your password and your username variable you’ll now have to remove that function because lower case symbols are now valid.   You’ll also warn your users that the login page is now case senstive.

FYI: In V5R1, the i5 added support for long passwords and by default it was set to off.  The QPWDLVL is what you need to change.  You can set the max length of the password using QPWDMAXLEN.   If you are using a custom login page you may need to rewrite it or switch to QSYS/QDSIGNON.  You will have to IPL the system after this change

Netbeans IDE for PHP on the iSeries better than Zend Studio

Sick of Zend Studio and Eclipse?   Do you think “Zend Studio for IBM i” sucks?  I was sick of it and found the Netbeans IDE and found its a great alternative to the Clunky Zend Studio mess.

What can Netbeans do?  Everything all in one!

PHP IDE – Code Highlighting, Auto completing etc…

IBMi DB2 Database access via the JTOpen

Source Control via SVN or GIT

Debugging via xdebug

FTP your files to the I5 after saving the file locally

to name a few features i was looking for in the Zend Studio for i, here’s what else it can do.

Get Started


Download Netbeans
Here’s the download link for Netbeans IDE.

SVN Setup
Learn how to setup subversion in Netbeans from their online tutorial.

Connecting to the AS400 / I5 / IBMi to run SQL from the IDE
To set up the database connection to the I5 you’ll need to download JTOPEN then go to the services tab (click ctrl+5 when in Netbeans), right click drivers, and click new drivers, click add and find the jt400.jar file from the JTOPEN download folder

After the driver is set up, right click it and click “connect using”, set the driver to JTOpen400 fill in the username and password and set the  url to jdbc:as400://as400servername/libraryname

Auto-FTP of files
Go over the tutorial on netbeans.org to setup a remote project so that your files are automatically FTP’d to your I5.


Alternative to System i Navigator

Why not use the System I Navigator?  Well I find it annoying the number of things I have to click before I can finally run a sql query.  Also I like having 1 application to do everything.  There are some times where I run a script in System i Navigator’s “Run SQL” window because it supports CL commands via “CL: <CL Command here>


Creating NFS Export on IBM iSeries

Steps to setup a NFS Export on I5 (as400/iseries/ibmi)

  1. Grant permissions on the directory to QNFSANON
  2. Open iSeries Navigator and Go to ServerName, Network, Servers, TCP/IP, right clicked NFS, select exports and added the directory and select QNFSANON as the Anonymous user, and selected “Add to list of permanently defined exports”.

These steps will allow the folder to be accessible to the webserver.

To learn how to do an NFS mount on your linux box, you’ll want to check out http://nfs.sourceforge.net/

NFS stands for Network File System.  A NFS export is used to share a IBMi IFS directory of the server with client servers that mount that directory.  You will have to setup permissions on that IFS directory on the Host machine so that the NFS Export can be mounted by a client machine, and you’ll have to setup permissions on the client server to make sure the correct users on the client server have access to the directory.  On the client server you mount the directory by

1) Type of file system NFS
2) The remote server and directory to mount MYAS400:/exports/mynfsexport
3) The local directory to mount over. /mount/as400export

Example linux command to mount as400 NFS to linux server

mount -o tcp myas400.example.com:/exports/mynfsexport /mount/as400export

iSeries Access 64 Bit Driver problems Segmentation Faults

Update: May 17 2016 – At common I found out that 64 bit issues should now all be resolved when using the latest PHP 5.6, the unixODBC manager 2.2.14+ and IBM i Access for Linux ODBC driver.  This was effecting PHP extension: PDO ODBC, and ODBC_* functions

PHP 5.6.x+
http://php.net/downloads.php

UnixODBC Manager
http://www.unixodbc.org/

IBM i Access ODBC Driver [for Linux] (ibm-iaccess-1.1.0.1-1.0.x86_64)
http://www-03.ibm.com/systems/power/software/i/access/linux.html

Kevin Adler’s Article from IBM
https://www.ibm.com/developerworks/ibmi/library/i-ibmi-access-client-solutions-linux/

Segmentation Fault Problem

I’ve had a lot of trouble with using the iSeries Access 64 bit odbc driver for Linux (iSeriesAccess-5.4.0-1.6.x86_64.rpm) on my RHEL linux box to connect toa  V5R4 iSeries server.  I switched back to using the 32 bit driver and saying i’ll wait until they update the 64 bit driver to work.

I was going to the PHP page and finding just a blank screen returned to the browser, which made it a pain to debug.  I found out after looking at the apache error log file in /var/log/httpd/error_log  that there were segmentation faults with the following error:

[notice] child pid XXXXX exit signal Segmentation fault (11)

To try and debug the PHP to see what causing the problem i put die(‘here’) statements in my code until i found out where the interpretor wasn’t getting to because of the seg fault.   I found out on the return of the Zend Framework’s PDO ODBC Fetchall statement the program was causing the seg fault.  I also noticed that this only occured when i had a null field in the DB2 udb for iSeries database.  The field was a char datatype.

If you’re having this problem i’d recommend just installing the 32 bit driver(iSeriesAccess-5.4.0-1.2.i386.rpm).

Debugging

Here’s how i understand requests get called in case you want to try and debug the problem:

  1. Request for page comes in from Apache
  2. PHP runs and makes db calls through  a DSN
  3. All db calls go through unixODBC manager
  4. The db call then goes through the IBM Client Access driver you have specified and executes the sql on the iSeries.
  5. If the driver has a problem with a null field it would create a segmentation fault.

Here’s a link to someone’s explanation of the problems using the 64 bit driver:

https://www.ibm.com/developerworks/forums/thread.jspa?messageID=14510046&#14510046

Need help understanding how to install the driver and setup all the linux files (/etc/odbc.ini /etc/odbcisnt.ini)

Here’s the link to the driver download

iSeries Access Linux Driver download

Remember to edit your
/etc/odbcinst.ini

[iSeries Access ODBC Driver]

Description &amp;amp;amp;amp;nbsp; &amp;amp;amp;amp;nbsp; &amp;amp;amp;amp;nbsp; &amp;amp;amp;amp;nbsp; &amp;amp;amp;amp;nbsp; &amp;amp;amp;amp;nbsp; = iSeries Access for Linux ODBC Driver
Driver &amp;amp;amp;amp;nbsp; &amp;amp;amp;amp;nbsp; &amp;amp;amp;amp;nbsp; &amp;amp;amp;amp;nbsp; &amp;amp;amp;amp;nbsp;= /opt/ibm/iSeriesAccess/lib/libcwbodbc.so
Setup &amp;amp;amp;amp;nbsp; &amp;amp;amp;amp;nbsp; &amp;amp;amp;amp;nbsp; &amp;amp;amp;amp;nbsp; &amp;amp;amp;amp;nbsp; = /opt/ibm/iSeriesAccess/lib/libcwbodbcs.so
NOTE1 &amp;amp;amp;amp;nbsp; &amp;amp;amp;amp;nbsp; &amp;amp;amp;amp;nbsp; &amp;amp;amp;amp;nbsp; &amp;amp;amp;amp;nbsp; = If using unixODBC 2.2.11 or later and you want the 32 and 64-bit ODBC drivers to share DSN's,
NOTE2 &amp;amp;amp;amp;nbsp; &amp;amp;amp;amp;nbsp; &amp;amp;amp;amp;nbsp; &amp;amp;amp;amp;nbsp; &amp;amp;amp;amp;nbsp; = the following Driver64/Setup64 keywords will provide that support.
Driver64 &amp;amp;amp;amp;nbsp; &amp;amp;amp;amp;nbsp; &amp;amp;amp;amp;nbsp; &amp;amp;amp;amp;nbsp; &amp;amp;amp;amp;nbsp; &amp;amp;amp;amp;nbsp; &amp;amp;amp;amp;nbsp; &amp;amp;amp;amp;nbsp;= /opt/ibm/iSeriesAccess/lib64/libcwbodbc.so
Setup64 &amp;amp;amp;amp;nbsp; &amp;amp;amp;amp;nbsp; &amp;amp;amp;amp;nbsp; &amp;amp;amp;amp;nbsp; = /opt/ibm/iSeriesAccess/lib64/libcwbodbcs.so
Threading &amp;amp;amp;amp;nbsp; &amp;amp;amp;amp;nbsp; &amp;amp;amp;amp;nbsp; &amp;amp;amp;amp;nbsp; &amp;amp;amp;amp;nbsp; &amp;amp;amp;amp;nbsp; &amp;amp;amp;amp;nbsp; = 2
DontDLClose &amp;amp;amp;amp;nbsp; &amp;amp;amp;amp;nbsp; &amp;amp;amp;amp;nbsp; &amp;amp;amp;amp;nbsp; &amp;amp;amp;amp;nbsp; &amp;amp;amp;amp;nbsp; = 1
UsageCount &amp;amp;amp;amp;nbsp; &amp;amp;amp;amp;nbsp; &amp;amp;amp;amp;nbsp; &amp;amp;amp;amp;nbsp; &amp;amp;amp;amp;nbsp; &amp;amp;amp;amp;nbsp; &amp;amp;amp;amp;nbsp;= 1

and /etc/odbc.ini

[iSeriesDSN]
Description = iSeries Access ODBC Driver DSN for iSeries
Driver = iSeries Access ODBC Driver
System = iSeriesSystemName
UserID =
Password =
Naming = 0
DefaultLibraries = QGPL
Database =
ConnectionType = 0
CommitMode = 2
ExtendedDynamic = 0
DefaultPkgLibrary = QGPL
DefaultPackage = A/DEFAULT(IBM),2,0,1,0,512
AllowDataCompression = 1
LibraryView = 0
AllowUnsupportedChar = 0
ForceTranslation = 0
Trace = 0

Giving credit to IBM for this example file

Common Fall Conference 2010 Day 2

Below are some notes i took while on my second day of common fall conference 2010.

Session 1 – DB2 for IBM i: Adv Functionality Sampler

Presented by Linda Swan

Presented by Linda Swan

DB2 for i Query Optimizer

lmswan@us.ibm.com

Referential Integrity

Customer file with Customer number, Invoices table with Customer Number.  You can turn on a constraint on the table forcing that there’s a customer number

Requires Parent Table to have a unique key

Child table has a Foreign key.

SQL –

Parent table: ALTER TABLE master ADD CONSTRAINT name PRIMARY KEY(custno)

Chlid table: ALTER TABLE Add contstraint name2 FOREIGN KEY (custnum) REFERENCES master (custno) ON DELETE CASCADE ON UPDATE RESTRICT

Insert Rule

no explicit rules but insert operations into dependent files are checked

Delete rule

RESTRICT – occurs before a trigger

CASCADE

SET NULL

SET DEFAULT

NO ACTION – occurs after a trigger

Update rule

restrict updates to a Parent key

RESTRICT

NO ACTION

Puts the policy in the database and doesn’t matter what application is using the table.  The Rules must be followed.

1 primary key, but can have multiple unique keys/parent keys

Journal Requirements

Restrict rule: journal not required

Any other rule:

Parent and Dependent files must be journaled to the same journal

Implicit commitment control is started

DB2 uses access paths and indexes for constraint enforcement

I Navigator will can show you constraints

Monitoring Exceptions in Applications

SQLCODEs 530,531,532

SQLSTATEs 23001, 23503,23504

Check Contraints (constraining a columns value)

ALTER TABLE Employee ADD CONSTRAINT SalaryChk CHECK (Salary<40000 AND Bonus <= Salary)

DB2 has to lock the table exclusively when adding this constraint to check the current data

The CHECK can have anything you would put in a where clause, but cannot reference columns outside of its table

Triggers

associated with a physical file

activated before or after

independent from application

Trigger could automatically send an email if someone’s order is $300 or more

Two Types

Native made through RPG

ADDPFTRG

CHGPFTRG

SQL triggers

Column level

Row level

Statement level

max 300 triggers per table

8 components

Base file

Trigger event

insert update or delete

trigger time

before or after

trigger program

Granularity

Transition Variables – Can reference the old row and new row

Transition Tables – Can reference the old table and new table (result set)

Triggered Action

triggers cannot pass paramters back

Column Level Security

SQL allows for granting only permissions to certain columns

2 phase commit allows to sync 2 boxes together

Stored Procedure

External

written in RPG

may or may not SQL

SQL

business logic is coded with SQL and doesn’t require compilation

V7R1 allows return of results sets to RPG

V7R1 allows arrays as an input to a stored procedure

Program Control operations in SQL

Conditional Logic, Declaration of Variables, Etc…

Joins

Unions – Union the sales for product xyz from Sales2008, Sales 2009, Sales2010

EXCEPT and INTERSECT

V7R1 has xml support, Global Variables. Array support in procedures, also 3part naming to allow you to connect to another db2 server inside a stored procedure.

ROW CHANGE TIMESTAMP added V6R1

V6R1 and V7R1 enhancements

http://www.ibm.com/systems/i/software/db2/

Sessions 2 – “I didn’t know you could do that with DB2 Web Query!”

Presented by Gene Cobb

cobb@us.ibm.com

DB2 Web Query rolled out in 2007, replaces the Query400 product

Partnership with infromation builders

Leaves the data on the IBM i

Creating a webserver

youri5:20001/HTTPADMIN

WRKACTJOB SBS(QHTTPSVR) to see the server is running

DB2 Web query can create reports and graphs against stored procedures that return result sets.

Displaying Images in your report

can display images of different products and have a link to a pdf document for that product

images and pdfs are stored in a web server

Report assistant lets you create a report and you create fields pointing to the images and the pdfs

Set the field to ‘<a href=”youri5:20001/MyPDFs/’ || PARTNUMBER || ‘.pdf”<img src=”youri5:20001/MyImages’|| PARTNUMBER ||’.gif” />’

Integrating OmniFind Text Search Server

DB2 interfaces for developing text search applications

Supports finding a DB2 record based on text contained ina  document that is stored in a DB2 column

Searches the database and your documents for a keyword.  Documents are stored in a LOB column of the database

Omnifind can search your IFS or LOB column

Integrating Web Services: SOAP Simple Object Access Protocol

Scott Klement’s free utility HTTPAPI

Showed an example of calling weather web service to get a 5 day forecast

Integrating Web Services (RESTful)

Representational State Transfer

architectural style not a standard

URL Drilldown (Google Map Integration)

uses the db2 web query iframe to change it to a google map.  You create a field that is put into the query parameter passed to maps.google.com?q= || ‘GOOGLECITY’

Integration using DB2 Web Query Report Integrator Toolkit

Abstraction layer that greatly simplifies report integration process.

Session 3 – PHP toolkit examples

Mike Pavlik

mike.p@zend.com

Zend Server is now the de facto standard, Zend Core is no longer supported after May 2011

PHP performance improved 30%-600%

Support for PHP 5.3

Code Tracing

Job Queue

Single apache server

PASE – Portable Application Solutions Environment

The toolkit might change in 2 months, don’t use for access db2,  use db2 adapter which is cross platform.

QTMHHTTP is the user in Zend Server

i5_adopt_authority can adopt other authorities while running

i5_data_area_read can read a data area a retrieve its value into PHP.  Showed an example of retrieving a company’s name from a data area

System Values

i5_get_system_value(“QMODEL”) will get the system value for qmodel and you can use that value now

Program Call can call RPG

Setup parameters in associative array, array with name, io, type, and length.  Equivalent to a plist

Prepare Program

Load parameters

Call the program, program, parameters, and return values

Spool file

you can grab a spool file and echo out all of its data

Mentioned midrange.com


Session 4 – PHP Batch Jobs on IBM i

Presented by Alan Seiden

Generate an XML based price list and FTP it to a customer each week at the same time.

Send email reports to administrators and errors to developers

Two methods

PHP-CLI (command line)

Requires knowledge of PASE or QSHELL environment

Automate the process with scheduled CL programs

located in /usr/local/zendsvr/bin/php-cli

Has no need for $_POST or $_GET and uses $argc and $argv

Apache doesn’t need to be running

Need not be in a web-accessible Alan likes to put them in /php/appname/myscript.php

Use a naming scheme thats easily to spot where you store your php scripts

ran on QSHELL or PASE

php-cli -v outputs PHP version information

php-cli -i outputs the equivalent of phpinfo()

php-cli -h to see all options (help)

CALL QP2TERM Terminal

CALL QP2SHELL PARM(‘/php/sendinvoice/php’ &EMAIL &NAME ‘Y’)

QP2SHELL2 same as QP2SHELL except it runs in ILE

C42PDF converts TIFFS to PDF

Using Qshell

STRQSH or QSH command can launch command line

QSH CMD(‘COMMAND’)

CALL PGM(QP2SHELL) PARM(‘phpdir/php-cli’ ‘htmldir/my.php’)

ADDJOBSCDE for your CL program

ADDJOBSCDE JOB(SNDPRICES) SCDDATE(*NONE) CMD (CALL PGM(MYLIB/PRICEPGM)) SCDDAY(*FRI) SCDTIME(’23:00′) FRQ(*WEEKLY) RCYACN(*NOSBM) JOBD(MYLIB/PRICEJOBD)

Zend Server Job Queue

Better for people not familar with command line OR needing more flexibility

Use complex parameters

ZendJobQueue().  queue->createHttpJob($url,$vars,$options) to start a job

Manage priorities

Zend Framework’s livedocx service can merge .doc templates with DB2 data

Excel Writer Pear is great for creating microsoft excel documents