Category Archives: IBM

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>


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