├── .travis.yml ├── tests ├── ADDONE.RPGLE ├── bootstrap.php ├── ToolkitApiTest │ ├── test-issue-44.php │ ├── HttpSuppTest.php │ ├── Int8ParamTest.php │ ├── Int16ParamTest.php │ ├── UInt8ParamTest.php │ ├── UInt16ParamTest.php │ ├── ProgramParameterTest.php │ └── ToolkitTest.php ├── config │ └── db.config.php.dist └── functional │ └── ToolkitTest.php ├── .gitignore ├── samples ├── omit.php ├── classic │ ├── display-library-list │ │ └── example.php │ ├── program-call-two-params │ │ └── example.php │ ├── service-program-call │ │ └── example.php │ └── program-call-data-structure-params │ │ └── example.php ├── data-structure.php ├── local-transport.php ├── display-library-list.php ├── simple-parameters.php ├── connect_pdo_odbc.php ├── data-structure-array-with-counter.php ├── README.md ├── ssh-transport.php ├── call-procedure.php ├── data-types.md └── options.php ├── ToolkitApi ├── ToolkitServiceSet.php ├── CW │ ├── cwclasses.php │ ├── I5Error.php │ ├── ToolkitServiceCw.php │ └── DataDescriptionPcml.php ├── iToolkitService.php ├── Int8Param.php ├── UInt8Param.php ├── Int16Param.php ├── UInt16Param.php ├── TmpUserSpace.php ├── ToolkitService.php ├── ObjectLists.php ├── DateTimeApi.php ├── LocalSupp.php ├── SystemValues.php ├── autoload.php ├── PdoSupp.php ├── ListFromApi.php ├── Odbcsupp.php ├── toolkit.ini.zs5 ├── SshSupp.php ├── toolkit.ini.zs6 ├── toolkit.ini ├── SpooledFiles.php ├── Db2supp.php ├── JobLogs.php ├── httpsupp.php ├── DataArea.php ├── DataQueue.php └── UserSpace.php ├── phpunit.xml.dist ├── phpunit.xml ├── README.md ├── LICENSE.txt └── composer.json /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | before_script: composer install 4 | 5 | script: phpunit --configuration phpunit.xml --testsuite ToolkitTests 6 | -------------------------------------------------------------------------------- /tests/ADDONE.RPGLE: -------------------------------------------------------------------------------- 1 | /FREE 2 | Dcl-Pi ADDONE; 3 | MyParm1 Int(10); 4 | End-Pi; 5 | MyParm1 += 1; 6 | Return; 7 | -------------------------------------------------------------------------------- /tests/bootstrap.php: -------------------------------------------------------------------------------- 1 | AddParameterChar('omit', 10, 'Param to allow omitting', 'OMITPARAM', ''); 11 | 12 | -------------------------------------------------------------------------------- /ToolkitApi/ToolkitServiceSet.php: -------------------------------------------------------------------------------- 1 | 13 | 14 | ./tests/ToolkitApiTest 15 | 16 | 17 | ./tests/functional 18 | 19 | -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 12 | 13 | 14 | ./tests/ToolkitApiTest 15 | 16 | 17 | ./tests/functional 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /tests/ToolkitApiTest/HttpSuppTest.php: -------------------------------------------------------------------------------- 1 | setIpc($ipc); 15 | 16 | $this->assertEquals($ipc, $httpsupp->getIpc()); 17 | } 18 | 19 | public function testIsIpcSet(): void 20 | { 21 | $ipc = 'test'; 22 | 23 | $httpsupp = new httpsupp(); 24 | $httpsupp->setIpc($ipc); 25 | 26 | $this->assertEquals($ipc, $httpsupp->getIpc()); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /tests/config/db.config.php.dist: -------------------------------------------------------------------------------- 1 | [ 4 | 'odbc' => [ 5 | 'dsn' => 'DSN=*LOCAL;', 6 | 'username' => 'MYUSER', 7 | 'password' => 'MYPASS', 8 | 'platform' => 'IbmDb2', 9 | 'platform_options' => [ 10 | 'quote_identifiers' => true, 11 | ], 12 | ], 13 | ], 14 | 'toolkit' => [ 15 | 'system' => [ 16 | 'XMLServiceLib' => 'QXMLSERV', 17 | 'HelperLib' => 'QXMLSERV', 18 | 'debug' => false, 19 | 'trace' => false, 20 | 'sbmjob_params' => 'QSYS/QSRVJOB/XTOOLKIT', 21 | 'stateless' => true, 22 | ], 23 | ], 24 | ]; -------------------------------------------------------------------------------- /ToolkitApi/Int8Param.php: -------------------------------------------------------------------------------- 1 | assertTrue($parameter instanceof ProgramParameter); 18 | 19 | $parameter = new Int8Param('in', 'comment 2', 'var2', 10); 20 | $this->assertTrue($parameter instanceof ProgramParameter); 21 | 22 | $parameter = new Int8Param('out', 'comment3', 'var3', 3); 23 | $this->assertTrue($parameter instanceof ProgramParameter); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /tests/ToolkitApiTest/Int16ParamTest.php: -------------------------------------------------------------------------------- 1 | assertTrue($parameter instanceof ProgramParameter); 18 | 19 | $parameter = new Int16Param('in', 'comment 2', 'var2', 10); 20 | $this->assertTrue($parameter instanceof ProgramParameter); 21 | 22 | $parameter = new Int16Param('out', 'comment3', 'var3', 3); 23 | $this->assertTrue($parameter instanceof ProgramParameter); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /tests/ToolkitApiTest/UInt8ParamTest.php: -------------------------------------------------------------------------------- 1 | assertTrue($parameter instanceof ProgramParameter); 18 | 19 | $parameter = new UInt8Param('in', 'comment 2', 'var2', 10); 20 | $this->assertTrue($parameter instanceof ProgramParameter); 21 | 22 | $parameter = new UInt8Param('out', 'comment3', 'var3', 3); 23 | $this->assertTrue($parameter instanceof ProgramParameter); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /tests/ToolkitApiTest/UInt16ParamTest.php: -------------------------------------------------------------------------------- 1 | assertTrue($parameter instanceof ProgramParameter); 18 | 19 | $parameter = new UInt16Param('in', 'comment 2', 'var2', 10); 20 | $this->assertTrue($parameter instanceof ProgramParameter); 21 | 22 | $parameter = new UInt16Param('out', 'comment3', 'var3', 3); 23 | $this->assertTrue($parameter instanceof ProgramParameter); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /ToolkitApi/TmpUserSpace.php: -------------------------------------------------------------------------------- 1 | CreateUserSpace($this->generate_name(), $UsLib, $DftUsSize)) { 19 | throw new \Exception($this->getError()); 20 | } else { 21 | $this->TMPUSName = sprintf("%-10s%-10s", $this->getUSName(), $UsLib); 22 | } 23 | 24 | return $this; 25 | } 26 | 27 | /** 28 | * @todo do not delete 29 | */ 30 | public function __destruct() 31 | { 32 | // $this->DeleteUserSpace(); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /samples/classic/display-library-list/example.php: -------------------------------------------------------------------------------- 1 | getMessage(), "\n"; 9 | exit(); 10 | } 11 | 12 | $obj->setToolkitServiceParams(array('InternalKey' => "/tmp/$user", 13 | 'debug' => true, 14 | 'plug' => "iPLUG32K")); 15 | $cmd = "addlible ZENDSVR"; 16 | $obj->CLCommand($cmd); 17 | echo "
";
18 | $rows = $obj->CLInteractiveCommand("DSPLIBL");
19 | /*$rows = $obj->CLInteractiveCommand("WRKSYSVAL OUTPUT(*PRINT)");*/
20 | if (!$rows) {
21 |     echo $obj->getLastError();
22 | } else {
23 |     var_dump($rows);
24 | }
25 | 
26 | echo "
"; 27 | 28 | /* Do not use the disconnect() function for "state full" connection */ 29 | $obj->disconnect(); 30 | -------------------------------------------------------------------------------- /samples/data-structure.php: -------------------------------------------------------------------------------- 1 | // Data structure is easy to add. It's just another parameter. 2 | 3 | $param = []; // initialize parameter array 4 | 5 | // add parameters as desired 6 | $param[] = $tk->AddParameterChar('in', 10,'Name', 'PTNAME', 'Fred'); 7 | $param[] = $tk->AddParameterChar('in', 25,'Address', 'PTADDR', '123 Toolkit Drive'); 8 | 9 | // DATA STRUCTURE 10 | // Define the data structure as an array of basic parameter types or other data structures 11 | $ds = []; 12 | $ds[] = $tk->AddParameterChar('in', 21,'Part', 'PTPRT', 'A123'); 13 | $ds[] = $tk->AddParameterChar('in', 3,'Vendor', 'PTVEN', '825'); 14 | $ds[] = $tk->AddParameterChar('out', 20,'Description', 'PTDES', $out); 15 | $ds[] = $tk->AddParameterZoned('out', 9, 2, 'Price', 'PTPRC', $out); 16 | 17 | // Add the data structure as just another element in your main parameter array. 18 | $param[] = $tk->AddDataStruct($ds, 'myds'); 19 | 20 | // Add additional regular parameters as needed 21 | $param[] = $tk->AddParameterZoned('in', 5, 2, 'Discount', 'PTDISC', '0.24'); 22 | -------------------------------------------------------------------------------- /samples/local-transport.php: -------------------------------------------------------------------------------- 1 | CLInteractiveCommand("DSPLIBL"); // just one type of command to run 16 | var_dump($res); 17 | } catch (Exception $e) { 18 | echo $e->getMessage(), "\n"; 19 | } 20 | -------------------------------------------------------------------------------- /samples/display-library-list.php: -------------------------------------------------------------------------------- 1 | getMessage(), "\n"; 14 | exit(); 15 | } 16 | 17 | $conn->setOptions(array('stateless' => true)); 18 | 19 | echo "
";
20 | $rows = $conn->CLInteractiveCommand("DSPLIBL");
21 | if (!$rows) {
22 |     echo $conn->getLastError();
23 | } else {
24 |     print_r($rows);
25 | }
26 | 
27 | echo "
"; 28 | ?> 29 | 30 | Output will look like: 31 |
Array
32 | (
33 |     [0] =>  5770SS1 V7R4M0  190621                    Library List                                          7/02/21 15:10:47        Page    1
34 |     [1] =>                           ASP
35 |     [2] =>    Library     Type       Device      Text Description
36 |     [3] =>    QSYS        SYS                    System Library
37 |     [4] =>    QSYS2       SYS                    System Library for CPI's
38 |     [5] =>    QHLPSYS     SYS
39 |     [6] =>    QUSRSYS     SYS                    System Library for Users
40 |     [7] =>    QGPL        USR                    General Purpose Library
41 |     [8] =>    QTEMP       USR
42 |     [9] =>                           * * * * *  E N D  O F  L I S T I N G  * * * * *
43 | )
44 | 
45 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | PHP Toolkit for IBM i 2 | ===================== 3 | 4 | [![Build Status](https://travis-ci.org/zendtech/IbmiToolkit.svg?branch=master)](https://travis-ci.org/zendtech/IbmiToolkit) 5 | 6 | For production systems please do not use the master branch. Instead use the latest 7 | [stable release](https://github.com/zendtech/IbmiToolkit/releases/latest). 8 | 9 | Introduction 10 | ------------ 11 | 12 | The PHP Toolkit for IBM i (Toolkit) is a PHP-based front end to [XMLSERVICE](http://www.youngiprofessionals.com/wiki/XMLSERVICE) that helps programmers call RPG and CL programs along with other native resources from within PHP. 13 | 14 | The Toolkit is open source and has been developed with help from Alan Seiden and the community. 15 | 16 | Discussion of the Toolkit takes place in GitHub Discussions: 17 | https://github.com/zendtech/IbmiToolkit/discussions 18 | 19 | Current Main Features: 20 | 21 | - Ability to call RPG, CL, and COBOL 22 | - Run interactive commands such as ‘wrkactjob’ 23 | - Transport-neutral, supporting DB2, ODBC, and HTTP, and others as needed 24 | - Compatibility wrapper to execute Easycom syntax 25 | - Support of all RPG parameter types, including data structures, packed decimal, and output parameters 26 | 27 | XMLSERVICE and the IBM i Toolkit are already shipped with Zend Server and Seiden CommunityPlus+ PHP. But being 28 | open source they can also be downloaded, installed, and upgraded separately. 29 | 30 | For examples, please visit: 31 | https://github.com/zendtech/IbmiToolkit/tree/master/samples 32 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2005-2014, Zend Technologies USA, Inc. 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, 5 | are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, this 11 | list of conditions and the following disclaimer in the documentation and/or 12 | other materials provided with the distribution. 13 | 14 | * Neither the name of the {organization} nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 22 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 25 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /ToolkitApi/ToolkitService.php: -------------------------------------------------------------------------------- 1 | =7.1", 20 | "ext-mbstring": "*", 21 | "ext-pcre": "*", 22 | "ext-pdo": "*", 23 | "ext-simplexml": "*" 24 | }, 25 | "suggest": { 26 | "ext-ibm_db2": "For the Db2 transport", 27 | "ext-odbc": "For the ODBC transport", 28 | "ext-pdo_ibm": "For the PDO Db2 transport", 29 | "ext-pdo_odbc": "For the PDO ODBC transport", 30 | "ext-pcntl": "For the local transport", 31 | "ext-ssh2": "For the SSH transport" 32 | }, 33 | "autoload": { 34 | "classmap": ["ToolkitApi/"], 35 | "psr-4": { 36 | "ToolkitApi\\":"ToolkitApi" 37 | } 38 | }, 39 | "autoload-dev": { 40 | "psr-4": { 41 | "ToolkitApi\\":"ToolkitApi", 42 | "ToolkitApiTest\\":"tests/ToolkitApiTest", 43 | "ToolkitFunctionalTest\\": "tests/functional" 44 | } 45 | }, 46 | "require-dev": { 47 | "phpunit/phpunit": "^11.0", 48 | "symfony/process": "^4.3" 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /samples/classic/program-call-two-params/example.php: -------------------------------------------------------------------------------- 1 | getMessage(), "\n"; 24 | exit(); 25 | } 26 | 27 | $ToolkitServiceObj->setToolkitServiceParams(array('InternalKey' => "/tmp/$user")); 28 | $code = $_POST ['code']; 29 | $desc = ' '; 30 | 31 | $param[] = $ToolkitServiceObj->AddParameterChar('both', 10, 'CODE', 'CODE', $code); 32 | $param[] = $ToolkitServiceObj->AddParameterChar('both', 10, 'DESC', 'DESC', $desc); 33 | $result = $ToolkitServiceObj->PgmCall("COMMONPGM", "ZENDSVR6", $param, null, null); 34 | if ($result) { 35 | showTable($result['io_param']); 36 | } else { 37 | echo "Execution failed."; 38 | } 39 | 40 | /* Do not use the disconnect() function for "state full" connection */ 41 | $ToolkitServiceObj->disconnect(); 42 | -------------------------------------------------------------------------------- /samples/classic/service-program-call/example.php: -------------------------------------------------------------------------------- 1 | getMessage(), "\n"; 10 | exit(); 11 | } 12 | $ToolkitServiceObj->setToolkitServiceParams(array('InternalKey' => "/tmp/$user")); 13 | $SysValueName = "QCCSID"; 14 | $Err = ' '; 15 | $SysValue = ' '; 16 | 17 | $param[] = $ToolkitServiceObj->AddParameterChar('both', 1, 'ErrorCode', 'errcode', $Err); 18 | $param[] = $ToolkitServiceObj->AddParameterChar('both', 10, 'SysValName', 'sysvalname', $SysValueName); 19 | $param[] = $ToolkitServiceObj->AddParameterChar('both', 1024, 'SysValue', 'sysvalue', $SysValue); 20 | $OutputParams = $ToolkitServiceObj->PgmCall('ZSXMLSRV', "ZENDSVR6", $param, NULL, array('func' => 'RTVSYSVAL')); 21 | if (isset($OutputParams['io_param']['sysvalname'])) { 22 | echo " System value " . $SysValueName . " = " . $OutputParams['io_param']['sysvalue']; 23 | } else 24 | echo " Operation failed. System value $SysValueName did not retrieve."; 25 | /*change parameter value and execute again PgmCall()*/ 26 | ProgramParameter::UpdateParameterValues($param, array("sysvalname" => "QLANGID")); 27 | $OutputParams = $ToolkitServiceObj->PgmCall('ZSXMLSRV', "ZENDSVR6", $param, NULL, array('func' => 'RTVSYSVAL')); 28 | if (isset($OutputParams['io_param']['sysvalname'])) { 29 | echo " System value " . $SysValueName . " = " . $OutputParams['io_param']['sysvalue']; 30 | } else { 31 | echo " Operation failed. System value $SysValueName did not retrieve."; 32 | } 33 | 34 | $ToolkitServiceObj->disconnect(); 35 | -------------------------------------------------------------------------------- /samples/simple-parameters.php: -------------------------------------------------------------------------------- 1 | setOptions(array('stateless'=>true)); 14 | 15 | // Define several input/output params 16 | // (To see all available data types, see: samples/data-types.md) 17 | $params = []; // start with empty array 18 | $params[] = $conn->AddParameterChar('in', 1,'Division', 'DIV', 'A'); 19 | $params[] = $conn->AddParameterChar('in', 6,'Product', 'PROD', '123456'); 20 | $params[] = $conn->AddParameterPackDec('both', 7, 2, 'Quantity', 'QTY', '4.53'); 21 | $params[] = $conn->AddParameterZoned('out', 5, 2, 'Price', 'PRICE', '0'); 22 | 23 | // Call program. 24 | // In this example, assume your program is MYLIB/MYPGM. 25 | $result = $conn->PgmCall('MYPGM', 'MYLIB', $params); 26 | 27 | if (!$result) { 28 | echo 'Error calling program. Code: ' . $conn->getErrorCode() . ' Msg: ' . $conn->getErrorMsg(); 29 | } else { 30 | echo 'Called program successfully.

'; 31 | // Parameter values that are I/O type "out" or "both" will be available in $result['io_param']. 32 | echo 'Input/output params: QTY: ' . $result['io_param']['QTY'] . ' PRICE: ' . $result['io_param']['PRICE'] . '
'; 33 | } 34 | 35 | /* 36 | The above will output something like: 37 | 38 | Called program successfully. 39 | 40 | Input/output params: QTY: 4.53 PRICE: 0.00 41 | 42 | */ 43 | -------------------------------------------------------------------------------- /samples/connect_pdo_odbc.php: -------------------------------------------------------------------------------- 1 | $persistence, 30 | PDO::ATTR_ERRMODE => PDO::ERRMODE_WARNING, 31 | )); 32 | } catch (PDOException $e) { 33 | die ('PDO connection failed: ' . $e->getMessage()); 34 | } 35 | 36 | // use your existing database connection when creating toolkit object 37 | $tkobj = ToolkitService::getInstance($dbconn, $namingMode, '', $dbConnectionType); 38 | 39 | // simplest is stateless mode, but could set InternalKey to a directory name instead for a stateful connection. 40 | $tkobj->setOptions(array('stateless' => true)); 41 | 42 | // (Example command to test toolkit) 43 | // run DSPLIBL and return screen as an array 44 | $result = $tkobj->ClInteractiveCommand('DSPLIBL'); 45 | print_r($result); 46 | -------------------------------------------------------------------------------- /samples/data-structure-array-with-counter.php: -------------------------------------------------------------------------------- 1 | AddParameterChar('in', 21,'Part', 'PTPRT', 'A123'); 17 | $ds[] = $conn->AddParameterChar('in', 3,'Vendor', 'PTVEN', '825'); 18 | $ds[] = $conn->AddParameterChar('out', 20,'Description', 'PTDES', $out); 19 | $ds[] = $conn->AddParameterZoned('out', 9, 2, 'Price', 'PTPRC', $out); 20 | 21 | // Multi-occurrence data structure with maximum dimension set to 10000 but whose final output count will be determined by MYCOUNTER. Name the counter whatever you wish. 22 | $param[] = $conn->AddDataStruct($ds, 'MULTDS') 23 | ->setParamDimension(10000) // if your RPG array has max of 10000 records 24 | ->setParamLabelCounted('MYCOUNTER'); // create your own counter name 25 | 26 | // COUNTVAR is a counter field. Value is set by RPG/COBOL program. Value will control the number of MULTDS fields that return (see data structure MULTDS below) 27 | // Counter variable doesn't have to match the counted() and counter() labels. 28 | $param[] = $conn->AddParameterZoned('both', 5, 0, 'how many MULTDS array elements actually return', 'COUNTVAR', 6) 29 | ->setParamLabelCounter('MYCOUNTER'); // match the counter name you defined on setParamLabelCounted() 30 | 31 | // The count identifier you specified ties them together. 32 | 33 | // Now when you call your RPG program, the output will be very efficient, because you'll receive only the number of records that the RPG program specified in the counter variable. 34 | 35 | ?> 36 | -------------------------------------------------------------------------------- /samples/README.md: -------------------------------------------------------------------------------- 1 | # PHP Toolkit sample scripts 2 | 3 | - [`call-procedure.php`](https://github.com/zendtech/IbmiToolkit/blob/master/samples/call-procedure.php): 4 | Complete example of calling an RPG procedure (service program) 5 | - [`connect_pdo_odbc.php`](https://github.com/zendtech/IbmiToolkit/blob/master/samples/connect_pdo_odbc.php): 6 | How to connect to the toolkit using PDO_ODBC 7 | - [`data-structure-array-with-counter.php`](https://github.com/zendtech/IbmiToolkit/blob/master/samples/data-structure-array-with-counter.php): 8 | Efficient way to receive an array of data back 9 | - [`data-types.md`](https://github.com/zendtech/IbmiToolkit/blob/master/samples/data-types.md): 10 | Reference guide to XMLSERVICE data types and equivalent PHP Toolkit functions 11 | - [`display-library-list.php`](https://github.com/zendtech/IbmiToolkit/blob/master/samples/display-library-list.php): 12 | Get results back from an "interactive/5250" CL command. 13 | - [`local-transport.php`](https://github.com/zendtech/IbmiToolkit/blob/master/samples/local-transport.php): 14 | Uses xmlservice-cli to make a program call without needing a database connection. 15 | - [`omit.php`](https://github.com/zendtech/IbmiToolkit/blob/master/samples/omit.php): 16 | When an RPG program makes a parameter optional by specifying `OPTION(*OMIT)`, this snippet shows how to to specify on the PHP side that the parameter will not be passed. 17 | - [`options.php`](https://github.com/zendtech/IbmiToolkit/blob/master/samples/options.php): 18 | Connection options, both basic and advanced. Recommended reading. 19 | - [`ssh-transport.php`](https://github.com/zendtech/IbmiToolkit/blob/master/samples/ssh-transport.php): 20 | Uses the SSH transport for connecting to the toolkit. Ideal for running the toolkit on a PC or other system and connecting to a remote IBM i. 21 | - [`simple-parameters.php`](https://github.com/zendtech/IbmiToolkit/blob/master/samples/simple-parameters.php): 22 | Example with ordinary parameters only; no data structures, arrays, or procedure return values. 23 | - [`classic` folder](https://github.com/zendtech/IbmiToolkit/tree/master/samples/classic): 24 | Contains the original Zend Server examples from 2011. For historical purposes only. 25 | -------------------------------------------------------------------------------- /ToolkitApi/ObjectLists.php: -------------------------------------------------------------------------------- 1 | ToolkitSrvObj = $ToolkitSrvObj; 23 | return $this; 24 | } else { 25 | return false; 26 | } 27 | } 28 | 29 | /** 30 | * @param string $object = *ALL, *name, *generic name 31 | * @param string $library = *ALL, *name, *generic name 32 | * @param string $objecttype = *ALL, *type 33 | * @return array|bool 34 | * @throws \Exception 35 | */ 36 | public function getObjectList($object = '*ALL', $library = '*LIBL', $objecttype = '*ALL') 37 | { 38 | $ObjName = $object; 39 | $ObjLib = $library; 40 | $ObjType = $objecttype; 41 | 42 | $this->TmpUserSpace = new TmpUserSpace($this->ToolkitSrvObj); 43 | 44 | $UsFullName = $this->TmpUserSpace->getUSFullName(); 45 | 46 | $params[] = $this->ToolkitSrvObj->AddParameterChar('in', 20, "User Space Name", 'userspacename', $UsFullName); 47 | $params[] = $this->ToolkitSrvObj->AddParameterChar('in', 10, "Object name", 'objectname', $ObjName); 48 | $params[] = $this->ToolkitSrvObj->AddParameterChar('in', 10, "Object library", 'objectlib', $ObjLib); 49 | $params[] = $this->ToolkitSrvObj->AddParameterChar('in', 10, "Object Type", 'objecttype', $ObjType); 50 | $this->ToolkitSrvObj->PgmCall(ZSTOOLKITPGM, $this->ToolkitSrvObj->getOption('HelperLib'), $params, NULL, array('func' => 'OBJLST')); 51 | 52 | $ObjList = $this->TmpUserSpace->ReadUserSpace(1, $this->TmpUserSpace->RetrieveUserSpaceSize()); 53 | $this->TmpUserSpace->DeleteUserSpace(); 54 | 55 | unset($this->TmpUserSpace); 56 | 57 | if (trim($ObjList)!='') { 58 | return (str_split($ObjList, $this->OBJLLISTREC_SIZE)); 59 | } else { 60 | return false; 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /ToolkitApi/CW/I5Error.php: -------------------------------------------------------------------------------- 1 | setI5Error(0, 0, '', ''); 36 | } 37 | 38 | /** 39 | * __toString will make it easy to output an error via echo or print 40 | * 41 | * @return string 42 | */ 43 | public function __toString() 44 | { 45 | $err = $this->getI5Error(); 46 | return "i5Error: num={$err['num']} cat={$err['cat']} msg=\"{$err['msg']}\" desc=\"{$err['desc']}\""; 47 | } 48 | 49 | /** 50 | * Set error information for last action. 51 | * 52 | * @param int $errNum Error number (according to old toolkit). Zero/false if no error 53 | * @param string $errCat Category of error 54 | * @param string $errMsg Error message (often a CPF code but sometimes just a message) 55 | * @param string $errDesc Longer description of error 56 | * @return void 57 | */ 58 | public function setI5Error($errNum, $errCat = I5_CAT_PHP, $errMsg = '', $errDesc = '') 59 | { 60 | // the array (eventually returned by i5_error() 61 | // likes to have both numeric and alphanumeric keys. 62 | $i5ErrorArray = array(0=>$errNum, 1=>$errCat, 2=>$errMsg, 3=>$errDesc, 63 | 'num'=>$errNum, 'cat'=>$errCat, 'msg'=>$errMsg, 'desc'=>$errDesc); 64 | 65 | self::$_i5Error = $i5ErrorArray; 66 | } 67 | 68 | /** 69 | * Return i5 error array for most recent action. 70 | * 71 | * @return array Error array for most recent action. 72 | */ 73 | public function getI5Error() 74 | { 75 | return self::$_i5Error; 76 | } 77 | } -------------------------------------------------------------------------------- /ToolkitApi/DateTimeApi.php: -------------------------------------------------------------------------------- 1 | ToolkitSrvObj = $ToolkitSrvObj ; 20 | } 21 | } 22 | 23 | /** 24 | * from 8-character *DTS format to 17-character full date and time 25 | * 26 | * @param $dtsDateTime 27 | * @return bool 28 | */ 29 | public function dtsToYymmdd($dtsDateTime) 30 | { 31 | $inputFormat = "*DTS"; // special system format, returned by some APIs. 32 | $outputFormat = "*YYMD"; // 17 chars long 33 | $outputVarname = 'datetimeOut'; 34 | 35 | $apiPgm = 'QWCCVTDT'; 36 | $apiLib = 'QSYS'; 37 | 38 | $paramXml = " 39 | $inputFormat 40 | 41 | 42 | $dtsDateTime 43 | 44 | 45 | $outputFormat 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | \n" . 54 | Toolkit::getErrorDataStructXml(5); // param number 5 55 | 56 | // pass param xml directly in. 57 | $retPgmArr = $this->ToolkitSrvObj->PgmCall($apiPgm, $apiLib, $paramXml); 58 | if ($this->ToolkitSrvObj->getErrorCode()) { 59 | return false; 60 | } 61 | 62 | $retArr = $retPgmArr['io_param'][$outputVarname]; 63 | 64 | return $retArr; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /tests/functional/ToolkitTest.php: -------------------------------------------------------------------------------- 1 | connectionOptions = $config['db']['odbc']; 29 | $this->toolkitOptions = $config['toolkit']; 30 | } 31 | 32 | /** 33 | * @throws \Exception 34 | */ 35 | public function testCanPassPdoOdbcObjectToToolkit(): void 36 | { 37 | $pdo = new \PDO( 38 | 'odbc:' . $this->connectionOptions['dsn'], 39 | $this->connectionOptions['username'], 40 | $this->connectionOptions['password'], 41 | [ 42 | 'platform' => $this->connectionOptions['platform'], 43 | 'platform_options' => [ 44 | 'quote_identifiers' => $this->connectionOptions['platform_options']['quote_identifiers'], 45 | ] 46 | ] 47 | ); 48 | 49 | $toolkit = new Toolkit($pdo, null, null, 'pdo'); 50 | $toolkit->setOptions($this->toolkitOptions); 51 | 52 | $this->assertInstanceOf(Toolkit::class, $toolkit); 53 | } 54 | 55 | /** 56 | * @throws \Exception 57 | */ 58 | public function testCanPassOdbcResourceToToolkit(): void 59 | { 60 | $connection = odbc_connect($this->connectionOptions['dsn'], $this->connectionOptions['username'], $this->connectionOptions['password']); 61 | 62 | if (!$connection) { 63 | throw new \Exception('Connection failed'); 64 | } 65 | $toolkit = new Toolkit($connection, null, null, 'odbc'); 66 | $toolkit->setOptions($this->toolkitOptions); 67 | 68 | $this->assertInstanceOf(Toolkit::class, $toolkit); 69 | } 70 | 71 | /** 72 | * @throws \Exception 73 | */ 74 | public function testCanPassOdbcConnectionParametersToToolkit(): void 75 | { 76 | $toolkit = new Toolkit( 77 | $this->connectionOptions['dsn'], 78 | $this->connectionOptions['username'], 79 | $this->connectionOptions['password'], 80 | 'odbc' 81 | ); 82 | $toolkit->setOptions($this->toolkitOptions); 83 | 84 | $this->assertInstanceOf(Toolkit::class, $toolkit); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /samples/classic/program-call-data-structure-params/example.php: -------------------------------------------------------------------------------- 1 | getMessage(), "\n"; 21 | exit(); 22 | } 23 | $ToolkitServiceObj->setToolkitServiceParams(array('InternalKey' => "/tmp/$user")); 24 | 25 | $IOParam['var1'] = array("in" => "Y", "out" => ""); 26 | $param[] = $ToolkitServiceObj->AddParameterChar('both', 1, 'INCHARA', 'var1', $IOParam['var1']['in']); 27 | $IOParam['var2'] = array("in" => "Z", "out" => ""); 28 | $param[] = $ToolkitServiceObj->AddParameterChar('both', 1, 'INCHARB', 'var2', $IOParam['var2']['in']); 29 | $IOParam['var3'] = array("in" => "001.0001", "out" => ""); 30 | $param[] = $ToolkitServiceObj->AddParameterPackDec('both', 7, 4, 'INDEC1', 'var3', '001.0001'); 31 | $IOParam['var4'] = array("in" => "0000000003.04", "out" => ""); 32 | $param[] = $ToolkitServiceObj->AddParameterPackDec('both', 12, 2, 'INDEC2', 'var4', '0000000003.04'); 33 | $IOParam['ds1'] = array("in" => "A", "out" => ""); 34 | $ds[] = $ToolkitServiceObj->AddParameterChar('both', 1, 'DSCHARA', 'ds1', 'A'); 35 | $IOParam['ds2'] = array("in" => "B", "out" => ""); 36 | $ds[] = $ToolkitServiceObj->AddParameterChar('both', 1, 'DSCHARB', 'ds2', 'B'); 37 | $IOParam['ds3'] = array("in" => "005.0007", "out" => ""); 38 | $ds[] = $ToolkitServiceObj->AddParameterPackDec('both', 7, 4, 'DSDEC1', 'ds3', '005.0007'); 39 | $IOParam['ds4'] = array("in" => "0000000006.08", "out" => ""); 40 | $ds[] = $ToolkitServiceObj->AddParameterPackDec('both', 12, 2, 'DSDEC1', 'ds4', '0000000006.08'); 41 | //$param[] = array('ds'=>$ds); 42 | $param[] = $ToolkitServiceObj->AddDataStruct($ds); 43 | $result = $ToolkitServiceObj->PgmCall('ZZCALL', "ZENDSVR6", $param, null, null); 44 | if ($result) { 45 | /*update parameters array by return values */ 46 | foreach ($IOParam as $key => &$element) { 47 | $element['out'] = $result['io_param'][$key]; 48 | } 49 | echo "
"; 50 | showTableWithHeader(array("Parameter name", "Input value", "Output value"), $IOParam); 51 | } else { 52 | echo "Execution failed."; 53 | } 54 | 55 | /* Do not use the disconnect() function for "state full" connection */ 56 | $ToolkitServiceObj->disconnect(); 57 | -------------------------------------------------------------------------------- /samples/ssh-transport.php: -------------------------------------------------------------------------------- 1 | "EADDEA1EB936B3C6F7D7E5A380B6BB607E71259D", 27 | // By default, the password like other transports is used by default, but you can use another method like so: 28 | "sshMethod" => "agent", 29 | // The password is ignored if not using password authentication. 30 | // No additional parameters are used for the agent. 31 | // This is ideal because you don't need to embed credentials. 32 | // Your development environment likely has a working agent too! 33 | // Want to use SSH keys? Try something like... 34 | /* 35 | "sshMethod" => "keyfile", 36 | "sshPublicKeyFile" => "/home/calvin/.ssh/id_rsa.pub", 37 | "sshPrivateKeyFile" => "/home/calvin/.ssh/id_rsa", 38 | // (optional) 39 | "sshPassphrase" => "dubnobasswithmyheadman" 40 | */ 41 | // Override the port number 42 | "sshPort" => 22, 43 | ); 44 | $tkobj = ToolkitService::getInstance("hostname", "username", '', "ssh", $options); 45 | $res = $tkobj->CLInteractiveCommand("DSPLIBL"); 46 | var_dump($res); 47 | // Setting it up yourself: 48 | $sshConn = ssh2_connect(hostname, 2222); // for example, custom port 49 | ssh2_auth_agent($sshConn, "username"); // simple example 50 | $tkobj = ToolkitService::getInstance($sshConn, "", "", "ssh"); 51 | var_dump($res); 52 | } catch (Exception $e) { 53 | echo $e->getMessage(), "\n"; 54 | } 55 | -------------------------------------------------------------------------------- /ToolkitApi/LocalSupp.php: -------------------------------------------------------------------------------- 1 | array("pipe", "r"), // stdin 25 | 1 => array("pipe", "w"), // stdout 26 | 2 => array("pipe", "w"), // stderr 27 | ); 28 | $proc = proc_open($this->getXmlserviceCliPath(), $descriptorspec, $pipes); 29 | if (!is_resource($proc)) { 30 | $this->setErrorCode("LOCAL_EXEC"); 31 | $this->setErrorMsg("error executing command on local system"); 32 | return false; 33 | } 34 | stream_set_blocking($pipes[0], true); // XXX 35 | $written = fwrite($pipes[0], $xmlIn); 36 | fclose($pipes[0]); // close stdin so the process starts to read 37 | $xmlOut = stream_get_contents($pipes[1]); 38 | fclose($pipes[1]); 39 | // XXX: Do something with stderr 40 | fclose($pipes[2]); 41 | proc_close($proc); 42 | return $xmlOut; 43 | } 44 | 45 | /** 46 | * @return string 47 | */ 48 | public function getErrorCode() 49 | { 50 | return $this->last_errorcode; 51 | } 52 | 53 | /** 54 | * @return string 55 | */ 56 | public function getErrorMsg() 57 | { 58 | return $this->last_errormsg; 59 | } 60 | 61 | /** 62 | * @param $errorCode 63 | */ 64 | protected function setErrorCode($errorCode) 65 | { 66 | $this->last_errorcode = $errorCode; 67 | } 68 | 69 | /** 70 | * @param $errorMsg 71 | */ 72 | protected function setErrorMsg($errorMsg) 73 | { 74 | $this->last_errormsg = $errorMsg; 75 | } 76 | 77 | /** 78 | * @param string $path 79 | */ 80 | public function setXmlserviceCliPath($path) 81 | { 82 | $this->xmlserviceCliPath = $path; 83 | } 84 | 85 | /** 86 | * @return null 87 | */ 88 | public function getXmlserviceCliPath() 89 | { 90 | return $this->xmlserviceCliPath; 91 | } 92 | 93 | private function checkCompat() 94 | { 95 | if (!extension_loaded("pcntl")) { 96 | $this->setErrorCode("PCNTL_NOT_LOADED"); 97 | $this->setErrorMsg("the process control extension isn't loaded"); 98 | return false; 99 | } 100 | return true; 101 | } 102 | 103 | /** 104 | * None of these options matter for a local transport. 105 | * 106 | * @param $server 107 | * @param $user 108 | * @param $password 109 | * @param $options 110 | * @return LocalSupp|bool 111 | */ 112 | public function connect($server, $user, $password, $options) 113 | { 114 | if (!$this->checkCompat()) { 115 | return false; 116 | } 117 | // stateless, we don't have to do anything 118 | return $this; 119 | } 120 | 121 | public function disconnect() 122 | { 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /samples/call-procedure.php: -------------------------------------------------------------------------------- 1 | getCode(); 13 | $msg = $e->getMessage(); 14 | 15 | switch ($code) { 16 | case 8001: 17 | // "Authorization failure on distributed database connection attempt" 18 | // Usually means a wrong DB2 user or password 19 | echo 'Could not connect due to wrong user or password.'; 20 | break; 21 | case 42705: 22 | echo 'Database not found. Try WRKRDBDIRE to check.'; 23 | break; 24 | default: 25 | echo 'Could not connect. Error: ' . $code . ' ' . $msg; 26 | break; 27 | } //(switch) 28 | die; // couldn't connect...handle this however you wish 29 | } //(try/catch) 30 | 31 | // set stateless mode for easy testing (no 'InternalKey' needed). 32 | // (setOptions() introduced in v1.4.0) 33 | $conn->setOptions(array('stateless'=>true)); 34 | 35 | /* If you wish to test this script but you don't have a real service program, 36 | * use parseOnly and parseDebugLevel as shown below. 37 | * No program will be called and you'll get your original values back. 38 | * Simply uncomment the next line to try this great testing feature of the toolkit. 39 | */ 40 | //$conn->setOptions(array('parseOnly'=>true, 'parseDebugLevel'=>1)); 41 | 42 | // define several input/output params 43 | $params = []; // start with empty array 44 | $params[] = $conn->AddParameterChar('in', 1,'Division', 'DIV', 'A'); 45 | $params[] = $conn->AddParameterChar('in', 6,'Product', 'PROD', '123456'); 46 | $params[] = $conn->AddParameterPackDec('both', 7, 2, 'Quantity', 'QTY', '4.53'); 47 | $params[] = $conn->AddParameterZoned('out', 5, 2, 'Price', 'PRICE', '0'); 48 | 49 | // define a procedure return param. Can be any type, even a data structure 50 | $retParam = $conn->AddParameterInt32('out', '4-byte int', 'MYRESULT', '13579'); 51 | 52 | /* Call service program procedure. 53 | * Procedure name is optional and specified in parameter 5, an array containing associative index 'func'. 54 | * Make sure your procedure name is 100% correct. It is case-sensitive. 55 | * If you get an error, look for your procedure name in the output from this command (replacing LIBNAME/PGMNAME with your library and program names): 56 | * DSPSRVPGM SRVPGM(LIBNAME/PGMNAME) DETAIL(*PROCEXP) 57 | * In this example, assume your program is MYLIB/MYPGM and has a procedure/function 'myproc' 58 | */ 59 | $result = $conn->PgmCall('MYPGM', 'MYLIB', $params, $retParam, array('func'=>'myproc')); 60 | 61 | if (!$result) { 62 | echo 'Error calling program. Code: ' . $conn->getErrorCode() . ' Msg: ' . $conn->getErrorMsg(); 63 | } 64 | 65 | echo 'Called program successfully.

'; 66 | echo 'Input/output params: QTY: ' . $result['io_param']['QTY'] . ' PRICE: ' . $result['io_param']['PRICE'] . '
'; 67 | echo 'Procedure return param MYRESULT: ' . $result['retvals']['MYRESULT']; 68 | 69 | /* 70 | The above will output something like: 71 | 72 | Called program successfully. 73 | 74 | Input/output params: QTY: 4.53 PRICE: 0.00 75 | Procedure return param MYRESULT: 13579 76 | 77 | */ 78 | -------------------------------------------------------------------------------- /ToolkitApi/SystemValues.php: -------------------------------------------------------------------------------- 1 | ToolkitSrvObj = $ToolkitSrvObj; 20 | return $this; 21 | } else { 22 | return false; 23 | } 24 | } 25 | 26 | /** 27 | * 28 | * @todo Deprecate setConnection in future. 29 | * 30 | * @param $dbname 31 | * @param $user 32 | * @param $pass 33 | */ 34 | public function setConnection ($dbname , $user, $pass) 35 | { 36 | if (!$this->ToolkitSrvObj instanceof Toolkit) { 37 | $this->ToolkitSrvObj = new Toolkit($dbname, $user, $pass); 38 | } 39 | } 40 | 41 | /** 42 | * @return array|bool 43 | */ 44 | public function systemValuesList() 45 | { 46 | if (!$this->ToolkitSrvObj instanceof Toolkit) { 47 | return false; 48 | } 49 | 50 | $tmparray = $this->ToolkitSrvObj->CLInteractiveCommand ('WRKSYSVAL OUTPUT(*PRINT)'); 51 | 52 | if (isset($tmparray)) { 53 | $i = 4; 54 | while (isset($tmparray [$i + 1])) { 55 | $tmparr = trim($tmparray [++$i]); 56 | if (substr($tmparr, 0, 1) == 'Q') { 57 | $len = strlen ($tmparr); 58 | $sysvals [] = array('Name' => substr ($tmparr, 0, 10), 59 | 'CurrentValue' => substr ($tmparr, 15, 32), 60 | 'ShippedValue' => substr ($tmparr, 47, 32), 61 | 'Description' => substr ($tmparr, 79, ($len - 79))); 62 | } 63 | } 64 | return $sysvals; 65 | } else { 66 | return false; 67 | } 68 | } 69 | 70 | /** 71 | * @todo QWCRSVAL to work with 2 tiers while retaining good performance 72 | * 73 | * @param $sysValueName 74 | */ 75 | public function getSystemValue($sysValueName) 76 | { 77 | if (!$this->ToolkitSrvObj instanceof Toolkit) { 78 | return false; 79 | } 80 | 81 | $Err = ' '; 82 | $SysValue = ' '; 83 | $params [] = $this->ToolkitSrvObj->AddParameterChar('both', 1, "ErrorCode", 'errorcode', $Err); 84 | $params [] = $this->ToolkitSrvObj->AddParameterChar('both', 10, "SysValName", 'sysvalname', $sysValueName); 85 | $params [] = $this->ToolkitSrvObj->AddParameterChar('both', 1024, "SysValue", 'sysval', $SysValue); 86 | $retArr = $this->ToolkitSrvObj->PgmCall(ZSTOOLKITPGM, $this->ToolkitSrvObj->getOption('HelperLib'), $params, NULL, array('func' => 'RTVSYSVAL')); 87 | 88 | if ($retArr !== false && isset($retArr['io_param'])) { 89 | $sysval = $retArr['io_param']; 90 | if (isset($sysval['sysvalname'])) { 91 | return $sysval['sysval']; 92 | } else { 93 | $this->setError($sysval['errorcode']); 94 | } 95 | } 96 | } 97 | 98 | /** 99 | * @return mixed 100 | */ 101 | public function getError() { 102 | return $this->ErrMessage; 103 | } 104 | 105 | /** 106 | * @param $errCode 107 | */ 108 | private function setError($errCode) 109 | { 110 | if ($errCode == '') /*clear error message*/ { 111 | $this->ErrMessage = ''; 112 | return; 113 | } 114 | 115 | if ($errCode == '1') { 116 | $this->ErrMessage = 'System value data is not available.'; 117 | } else { 118 | if ($errCode == '2') { 119 | $this->ErrMessage = 'System value can not be retrieved. '; 120 | } 121 | } 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /ToolkitApi/autoload.php: -------------------------------------------------------------------------------- 1 | __DIR__ . DIRECTORY_SEPARATOR . 'Toolkit.php', 8 | 'ToolkitApi\ToolkitInterface' => __DIR__ . DIRECTORY_SEPARATOR . 'ToolkitInterface.php', 9 | 'ToolkitApi\db2supp' => __DIR__ . DIRECTORY_SEPARATOR . 'Db2supp.php', 10 | 'ToolkitApi\httpsupp' => __DIR__ . DIRECTORY_SEPARATOR . 'httpsupp.php', 11 | 'ToolkitApi\SshSupp' => __DIR__ . DIRECTORY_SEPARATOR . 'SshSupp.php', 12 | 'ToolkitApi\LocalSupp' => __DIR__ . DIRECTORY_SEPARATOR . 'LocalSupp.php', 13 | 'ToolkitApi\DateTimeApi' => __DIR__ . DIRECTORY_SEPARATOR . 'DateTimeApi.php', 14 | 'ToolkitApi\ListFromApi' => __DIR__ . DIRECTORY_SEPARATOR . 'ListFromApi.php', 15 | 'ToolkitApi\UserSpace' => __DIR__ . DIRECTORY_SEPARATOR . 'UserSpace.php', 16 | 'ToolkitApi\TmpUserSpace' => __DIR__ . DIRECTORY_SEPARATOR . 'TmpUserSpace.php', 17 | 'ToolkitApi\DataQueue' => __DIR__ . DIRECTORY_SEPARATOR . 'DataQueue.php', 18 | 'ToolkitApi\SpooledFiles' => __DIR__ . DIRECTORY_SEPARATOR . 'SpooledFiles.php', 19 | 'ToolkitApi\JobLogs' => __DIR__ . DIRECTORY_SEPARATOR . 'JobLogs.php', 20 | 'ToolkitApi\ObjectLists' => __DIR__ . DIRECTORY_SEPARATOR . 'ObjectLists.php', 21 | 'ToolkitApi\SystemValues' => __DIR__ . DIRECTORY_SEPARATOR . 'SystemValues.php', 22 | 'ToolkitApi\DataArea' => __DIR__ . DIRECTORY_SEPARATOR . 'DataArea.php', 23 | 'ToolkitApi\odbcsupp' => __DIR__ . DIRECTORY_SEPARATOR . 'Odbcsupp.php', 24 | 'ToolkitApi\PdoSupp' => __DIR__ . DIRECTORY_SEPARATOR . 'PdoSupp.php', 25 | 'ToolkitApi\ToolkitPcml' => __DIR__ . DIRECTORY_SEPARATOR . 'ToolkitPCML.php', 26 | 'ToolkitApi\ProgramParameter' => __DIR__ . DIRECTORY_SEPARATOR . 'ToolkitServiceParameter.php', 27 | 'ToolkitApi\DataStructure' => __DIR__ . DIRECTORY_SEPARATOR . 'ToolkitServiceParameter.php', 28 | 'ToolkitApi\CharParam' => __DIR__ . DIRECTORY_SEPARATOR . 'ToolkitServiceParameter.php', 29 | 'ToolkitApi\ZonedParam' => __DIR__ . DIRECTORY_SEPARATOR . 'ToolkitServiceParameter.php', 30 | 'ToolkitApi\PackedDecParam' => __DIR__ . DIRECTORY_SEPARATOR . 'ToolkitServiceParameter.php', 31 | 'ToolkitApi\Int32Param' => __DIR__ . DIRECTORY_SEPARATOR . 'ToolkitServiceParameter.php', 32 | 'ToolkitApi\SizeParam' => __DIR__ . DIRECTORY_SEPARATOR . 'ToolkitServiceParameter.php', 33 | 'ToolkitApi\SizePackParam' => __DIR__ . DIRECTORY_SEPARATOR . 'ToolkitServiceParameter.php', 34 | 'ToolkitApi\Int64Param' => __DIR__ . DIRECTORY_SEPARATOR . 'ToolkitServiceParameter.php', 35 | 'ToolkitApi\UInt32Param' => __DIR__ . DIRECTORY_SEPARATOR . 'ToolkitServiceParameter.php', 36 | 'ToolkitApi\UInt64Param' => __DIR__ . DIRECTORY_SEPARATOR . 'ToolkitServiceParameter.php', 37 | 'ToolkitApi\FloatParam' => __DIR__ . DIRECTORY_SEPARATOR . 'ToolkitServiceParameter.php', 38 | 'ToolkitApi\RealParam' => __DIR__ . DIRECTORY_SEPARATOR . 'ToolkitServiceParameter.php', 39 | 'ToolkitApi\HoleParam' => __DIR__ . DIRECTORY_SEPARATOR . 'ToolkitServiceParameter.php', 40 | 'ToolkitApi\BinParam' => __DIR__ . DIRECTORY_SEPARATOR . 'ToolkitServiceParameter.php', 41 | 'ToolkitApi\XMLWrapper' => __DIR__ . DIRECTORY_SEPARATOR . 'ToolkitServiceXML.php', 42 | 'ToolkitApi\CW\ToolkitServiceCw' => __DIR__ . DIRECTORY_SEPARATOR . 'CW' . DIRECTORY_SEPARATOR . 'ToolkitServiceCw.php', 43 | 'ToolkitApi\CW\I5Error' => __DIR__ . DIRECTORY_SEPARATOR . 'CW' . DIRECTORY_SEPARATOR . 'I5Error.php', 44 | 'ToolkitApi\CW\DataDescription' => __DIR__ . DIRECTORY_SEPARATOR . 'CW' . DIRECTORY_SEPARATOR . 'DataDescription.php', 45 | 'ToolkitApi\CW\DataDescriptionPcml' => __DIR__ . DIRECTORY_SEPARATOR . 'CW' . DIRECTORY_SEPARATOR . 'DataDescriptionPcml.php', 46 | 'ToolkitApi\Int8Param' => __DIR__ . DIRECTORY_SEPARATOR . 'Int8Param.php', 47 | 'ToolkitApi\UInt8Param' => __DIR__ . DIRECTORY_SEPARATOR . 'UInt8Param.php', 48 | 'ToolkitApi\Int16Param' => __DIR__ . DIRECTORY_SEPARATOR . 'Int16Param.php', 49 | 'ToolkitApi\UInt16Param' => __DIR__ . DIRECTORY_SEPARATOR . 'UInt16Param.php', 50 | ); 51 | 52 | if (array_key_exists($class, $classmap)) { 53 | $file = $classmap[$class]; 54 | if (file_exists($file)) { 55 | require_once $file; 56 | } 57 | } 58 | 59 | return; 60 | }); 61 | -------------------------------------------------------------------------------- /tests/ToolkitApiTest/ProgramParameterTest.php: -------------------------------------------------------------------------------- 1 | size = 20; 31 | $this->type = sprintf('%dB', $this->size); 32 | $this->io = 'both'; 33 | $this->comment = 'p1'; 34 | $this->var = 'test'; 35 | $this->data = 'off'; 36 | $this->varying = 0; 37 | $this->dim = ''; 38 | $this->by = false; 39 | $this->programParameter = new ProgramParameter( 40 | $this->type, 41 | $this->io, 42 | $this->comment, 43 | $this->var, 44 | $this->data, 45 | $this->varying, 46 | $this->dim, 47 | $this->by 48 | ); 49 | } 50 | 51 | public function testCanGetParameterProperties(): void 52 | { 53 | $data1 = $this->programParameter->getParamProperities(); 54 | $data2 = array( 55 | 'type' => $this->type, 56 | 'io' => $this->io, 57 | 'comment' => $this->comment, 58 | 'var' => $this->var, 59 | 'data' => $this->data, 60 | 'varying' => $this->varying, 61 | 'dim' => $this->dim, 62 | 'by' => $this->by, 63 | 'array' => false, 64 | 'setlen' => null, 65 | 'len' => null, 66 | 'dou' => '', 67 | 'enddo' => '', 68 | 'ccsidBefore' => '', 69 | 'ccsidAfter' => '', 70 | 'useHex' => false, 71 | ); 72 | 73 | $this->assertEquals($data1, $data2); 74 | } 75 | 76 | public function testGetParameterPropertiesFacadeMethodReturnsSameAsRealMethod(): void 77 | { 78 | $data1 = $this->programParameter->getParamProperities(); 79 | $data2 = $this->programParameter->getParamProperties(); 80 | 81 | $this->assertEquals($data1, $data2); 82 | } 83 | 84 | 85 | } 86 | 87 | /*class DataStructureTest extends \PHPUnit_Framework_TestCase 88 | { 89 | 90 | } 91 | 92 | class CharParamTest extends \PHPUnit_Framework_TestCase 93 | { 94 | 95 | } 96 | 97 | class ZonedParamTest extends \PHPUnit_Framework_TestCase 98 | { 99 | 100 | } 101 | 102 | class PackedDecParamTest extends \PHPUnit_Framework_TestCase 103 | { 104 | 105 | } 106 | 107 | class Int32ParamTest extends \PHPUnit_Framework_TestCase 108 | { 109 | 110 | } 111 | 112 | class SizeParamTest extends \PHPUnit_Framework_TestCase 113 | { 114 | 115 | } 116 | 117 | class SizePackParamTest extends \PHPUnit_Framework_TestCase 118 | { 119 | 120 | } 121 | 122 | class Int64ParamTest extends \PHPUnit_Framework_TestCase 123 | { 124 | 125 | } 126 | 127 | class UInt32ParamTest extends \PHPUnit_Framework_TestCase 128 | { 129 | 130 | } 131 | 132 | class UInt64ParamTest extends \PHPUnit_Framework_TestCase 133 | { 134 | 135 | } 136 | 137 | class FloatParamTest extends \PHPUnit_Framework_TestCase 138 | { 139 | 140 | } 141 | 142 | class RealParamTest extends \PHPUnit_Framework_TestCase 143 | { 144 | 145 | } 146 | 147 | class HoleParamTest extends \PHPUnit_Framework_TestCase 148 | { 149 | public function testCanCreateInstance() 150 | { 151 | 152 | } 153 | }*/ 154 | 155 | /** 156 | * Class BinParamTest 157 | * @package ToolkitApiTest 158 | */ 159 | final class BinParamTest extends TestCase 160 | { 161 | /** 162 | * @var BinParam $binParam 163 | */ 164 | protected $binParam; 165 | 166 | /** 167 | * @var ProgramParameter $programParameter 168 | */ 169 | protected $programParameter; 170 | 171 | protected function setUp(): void 172 | { 173 | $size = 20; 174 | $this->binParam = new BinParam('both', $size, 'UncodeSample', 'p1', 'test'); 175 | $type = sprintf('%dB', $size); 176 | $this->programParameter = new ProgramParameter($type, 'both', 'p1', 'test', 'off', 0, '', false); 177 | } 178 | 179 | public function testCanConvertBinaryToString(): void 180 | { 181 | $hex = '74657374'; 182 | $data1 = $this->binParam->bin2str($hex); 183 | $data2 = $this->programParameter->bin2str($hex); 184 | 185 | $this->assertEquals($data1, $data2); 186 | } 187 | } 188 | -------------------------------------------------------------------------------- /ToolkitApi/PdoSupp.php: -------------------------------------------------------------------------------- 1 | setError(); 39 | return false; 40 | } 41 | 42 | $conn = new PDO($database, $user, $password, $options); 43 | 44 | if (!$conn instanceof PDO) { 45 | $this->setError(); 46 | return false; 47 | } 48 | 49 | return $conn; 50 | } 51 | 52 | /** 53 | * PdoSupp constructor. 54 | * @param PDO $pdo 55 | */ 56 | public function __construct(PDO $pdo) 57 | { 58 | $this->pdo = $pdo; 59 | } 60 | 61 | /** 62 | * @param PDO $conn 63 | */ 64 | public function disconnect($conn) 65 | { 66 | if ($conn instanceof PDO) { 67 | $conn = null; 68 | } 69 | } 70 | 71 | /** 72 | * @param PDO|null $conn 73 | */ 74 | protected function setError($conn = null) 75 | { 76 | if ($conn) { 77 | $this->setErrorCode($conn->errorCode()); 78 | $this->setErrorMsg(implode('|', $conn->errorInfo())); 79 | } else { 80 | $this->setErrorCode($this->pdo->errorCode()); 81 | $this->setErrorMsg(implode('|', $this->pdo->errorInfo())); 82 | } 83 | } 84 | 85 | /** 86 | * @param string $errorCode 87 | */ 88 | protected function setErrorCode($errorCode) 89 | { 90 | $this->last_errorcode = $errorCode; 91 | } 92 | 93 | /** 94 | * @return string 95 | */ 96 | public function getErrorCode() 97 | { 98 | return $this->last_errorcode; 99 | } 100 | 101 | /** 102 | * @param string $errorMsg 103 | */ 104 | protected function setErrorMsg($errorMsg) 105 | { 106 | $this->last_errormsg = $errorMsg; 107 | } 108 | 109 | /** 110 | * @return string 111 | */ 112 | public function getErrorMsg() 113 | { 114 | return $this->last_errormsg; 115 | } 116 | 117 | /** 118 | * this function used for special stored procedure call only 119 | * 120 | * @param PDO $conn 121 | * @param string $stmt 122 | * @param array $bindArray 123 | * @return string 124 | */ 125 | public function execXMLStoredProcedure($conn, $stmt, $bindArray) 126 | { 127 | $statement = $conn->prepare($stmt); 128 | 129 | if (!$statement) { 130 | $this->setError($conn); 131 | return false; 132 | } 133 | 134 | $result = $statement->execute(array( 135 | $bindArray['internalKey'], 136 | $bindArray['controlKey'], 137 | $bindArray['inputXml'] 138 | )); 139 | 140 | if (!$result) { 141 | $this->setError($conn); 142 | return "PDO error code: " . $this->pdo->errorCode() . ' msg: ' . $this->pdo->errorInfo(); 143 | } 144 | 145 | $outputXml = ''; 146 | 147 | if (!$bindArray['disconnect']) { // a disconnect request won't return data 148 | // Loop through rows, concatenating XML into a final XML string. 149 | foreach ($statement->fetchAll() as $row) { 150 | // for each row, get XML string from first and only array element, 151 | // no matter whether assoc or numeric key 152 | $xmlChunk = reset($row); 153 | if ($xmlChunk) { 154 | // Remove any "garbage" from after ending tag (there used to be an ODBC clob issue) 155 | if (strstr($xmlChunk , "")) { 156 | $pos = strpos($xmlChunk, ""); 157 | $pos += strlen(""); 158 | $outputXml .= substr($xmlChunk, 0, $pos); 159 | break; 160 | } else { 161 | $outputXml .= $xmlChunk; 162 | } 163 | } 164 | } 165 | } 166 | 167 | return $outputXml; 168 | } 169 | 170 | /** 171 | * @param PDO $conn 172 | * @param string $stmt 173 | * @return array 174 | */ 175 | public function executeQuery($conn, $stmt) 176 | { 177 | $txt = array(); 178 | $statement = $conn->prepare($stmt); 179 | $result = $statement->execute(); 180 | 181 | if (!$result) { 182 | $this->setError($conn); 183 | return $txt; 184 | } 185 | foreach ($statement->fetchAll() as $row) { 186 | $txt[] = $row; 187 | } 188 | 189 | return $txt; 190 | } 191 | } 192 | -------------------------------------------------------------------------------- /ToolkitApi/ListFromApi.php: -------------------------------------------------------------------------------- 1 | ..... that we'll wrap in a receiver variable. At this time it must be an XML string. 12 | 13 | // listinfo: totalRecords, firstRecordNumber, requestHandle. if firstRec... < totalRecords then can continue. 14 | // return I5_ERR_BEOF when went past last record. get CPF GUI0006 when used invalid record#. 15 | 16 | /** 17 | * @param $requestHandle 18 | * @param $totalRecords 19 | * @param $receiverDs 20 | * @param $lengthOfReceiverVariable 21 | * @param ToolkitInterface $ToolkitSrvObj 22 | */ 23 | public function __construct($requestHandle, $totalRecords, $receiverDs, $lengthOfReceiverVariable, ?ToolkitInterface $ToolkitSrvObj = null) 24 | { 25 | if ($ToolkitSrvObj instanceof Toolkit) { 26 | $this->ToolkitSrvObj = $ToolkitSrvObj; 27 | } 28 | 29 | $this->_requestHandle = $requestHandle; 30 | $this->_receiverSize = $lengthOfReceiverVariable; 31 | $this->_totalRecords = $totalRecords; 32 | $this->_nextRecordToRequest = 1; // will request record #1 when someone asks 33 | $this->_receiverDs = $receiverDs; // will request record #1 when someone asks 34 | } 35 | 36 | /** 37 | * @return ToolkitInterface 38 | */ 39 | public function getConn() 40 | { 41 | return $this->ToolkitSrvObj; 42 | } 43 | 44 | /** 45 | * Call QGYGTLE (get list entry) API using handle and "next record to request." 46 | * 47 | * Note: if get timing problems where no records are returned: 48 | * Embed the call to the QGYGTLE API in a do-loop that loops until a record is returned. 49 | * 50 | * @return bool Return false when run out of records (get GUI0006 error code). 51 | */ 52 | public function getNextEntry() 53 | { 54 | $apiPgm = 'QGYGTLE'; 55 | $apiLib = 'QSYS'; 56 | 57 | $receiverDs = $this->_receiverDs; 58 | $requestHandle = $this->_requestHandle; 59 | $lengthOfReceiverVariable = $this->_receiverSize; 60 | $nextRecordToRequest = $this->_nextRecordToRequest++; // assign to me, then increment for next time 61 | 62 | $outputVarname = 'receiver'; 63 | 64 | $lenLabel= 'size' . $outputVarname; 65 | 66 | $paramXml = " 67 | 68 | $receiverDs 69 | 70 | 71 | 72 | $lengthOfReceiverVariable 73 | 74 | 75 | $requestHandle 76 | \n" . $this->ToolkitSrvObj->getListInfoApiXml(4) . "\n" . 77 | " 78 | 1 79 | 80 | 81 | $nextRecordToRequest 82 | \n" . 83 | Toolkit::getErrorDataStructXmlWithCode(7); // param number 7 84 | 85 | // was getErrorDataStructXml 86 | // pass param xml directly in. 87 | $retPgmArr = $this->ToolkitSrvObj->PgmCall($apiPgm, $apiLib, $paramXml); 88 | 89 | if ($this->ToolkitSrvObj->getErrorCode()) { 90 | return false; 91 | } 92 | 93 | /* Even when no error reported by XMLSERVICE (->error), 94 | * we may get a GUI0006 in DS error structure exeption code, since we 95 | * supplied a full error data structure above (getErrorDataStructXmlWithCode). 96 | */ 97 | $apiErrCode = $retPgmArr['io_param']['errorDs']['exceptId']; 98 | 99 | if ($apiErrCode != '0000000') { 100 | // Note: caller can check for GUI0006 and GUI0001 (expected when go past last record) vs. any other error (not expected) 101 | $this->ToolkitSrvObj->setErrorCode($apiErrCode); 102 | return false; 103 | } 104 | 105 | $retArr = $retPgmArr['io_param'][$outputVarname]; 106 | 107 | return $retArr; 108 | } 109 | 110 | /** 111 | * close the list 112 | * 113 | * @return bool 114 | */ 115 | public function close() 116 | { 117 | // call QGYCLST, the "close list" api. 118 | $apiPgm = 'QGYCLST'; 119 | $apiLib = 'QSYS'; 120 | 121 | $requestHandle = $this->_requestHandle; 122 | 123 | $paramXml = " 124 | $requestHandle 125 | \n" . Toolkit::getErrorDataStructXml(2); // param number 2 126 | 127 | // pass param xml directly in. 128 | $this->ToolkitSrvObj->PgmCall($apiPgm, $apiLib, $paramXml); 129 | 130 | // GUI0006 means end of list 131 | if ($this->ToolkitSrvObj->getErrorCode()) { 132 | return false; 133 | } else { 134 | return true; 135 | } 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /samples/data-types.md: -------------------------------------------------------------------------------- 1 | # Data Types for XMLSERVICE and PHP Toolkit # 2 | 3 | This reference, begun by Tony Cairns while at IBM, used to be housed at `http://www.youngiprofessionals.com/wiki/index.php/XMLService/DataTypes`, but that site is no longer available, so we have made it available here, and added to it. 4 | 5 | ## Data Type Equivalents for C / RPG / XMLSERVICE XML / SQL ## 6 | 7 | ``` 8 | C types RPG types XMLSERVICE types SQL types 9 | =============== ============================ ================================================ ========= 10 | char D mychar 32a CHAR(32) 11 | varchar2 D myvchar2 32a varying VARCHAR(32) 12 | varchar4 D myvchar4 32a varying(4) 13 | packed D mydec 12p 2 DECIMAL(12,2) 14 | zoned D myzone 12s 2 NUMERIC(12,2) 15 | float D myfloat 4f FLOAT 16 | real/double D myreal 8f DOUBLE 17 | binary D mybin (any) F1F2F3 BINARY 18 | hole (no out) D myhole (any) 19 | boolean D mybool 1n CHAR(4) 20 | time D mytime T timfmt(*iso) 09.45.29 TIME 21 | timestamp D mystamp Z 2011-12-29-12.45.29.000000 TIMESTAMP 22 | date D mydate D datfmt(*iso) 2009-05-11 DATE 23 | int8/byte D myint8 3i 0 TINYINT (unsupported DB2) 24 | int16/short D myint16 5i 0 (4b 0) SMALLINT 25 | int32/int D myint32 10i 0 (9b 0) INTEGER 26 | int64/longlong D myint64 20i 0 BIGINT 27 | uint8/ubyte D myuint8 3u 0 28 | uint16/ushort D myuint16 5u 0 29 | uint32/uint D myuint32 10u 0 30 | uint64/ulonglong D myuint64 20u 0 31 | [indicator] D myind 1a 32 | 33 | ``` 34 | * Notes on the two sizes of VARCHAR: 35 | * varchar2: accommodates a string of 1-65535 bytes. 36 | * varchar4: use if string may be larger than 65535 bytes. 37 | * More information about varchar data types: 38 | * https://www.ibm.com/docs/en/i/7.5?topic=keywords-varcharlength-2-4 [https://www.ibm.com/docs/en/i/7.5?topic=keywords-varcharlength-2-4] 39 | * https://www.ibm.com/docs/en/i/7.5?topic=type-variable-length-character-graphic-ucs-2-formats [https://www.ibm.com/docs/en/i/7.5?topic=type-variable-length-character-graphic-ucs-2-formats] 40 | * Note on TIMFMT and DATFMT: see your own RPG source code for the format required by the parameter. 41 | * We assume a default of *ISO if not specified. 42 | * Example: https://github.com/IBM/xmlservice/blob/84de343df1dc513d5774f5c1f470ada6701f6488/src/zzvlad.rpgle#L6 43 | * Reference for date formats: https://www.ibm.com/docs/en/i/7.1?topic=80-datfmt-date-format-keyword-display-files 44 | 45 | 46 | ## PHP Toolkit Functions That Implement the XMLSERVICE Data Types Shown Above ### 47 | 48 | ``` 49 | toolkit method/class type i/o comment varName value 50 | ======================= ===== ====== ======= ======== ===== 51 | $tk->AddParameterChar ( "both",32, "char", "mychar", ""); 52 | $tk->AddParameterChar ( "both",32, "varchar2", "myvchar2", "", "on"); 53 | $tk->AddParameterChar ( "both",32, "varchar4", "myvchar4", "", 4); 54 | $tk->AddParameterZoned ( "both",12,2,"packed", "mydec", 0.0); 55 | $tk->AddParameterPackDec( "both",12,2,"zoned", "myzone", 0.0); 56 | $tk->AddParameterFloat ( "both", "float", "myfloat", 0.0); 57 | $tk->AddParameterReal ( "both", "real", "myreal", 0.0); 58 | $tk->AddParameterBin ( "both", 9, "binary", "mybin", bin2hex(0xF1F2F3)); // to binary pack("H*", $hex) 59 | $tk->AddParameterHole ( 40, "hole" ); // no output (zero input) 60 | $tk->AddParameterChar ( "both", 4, "boolean", "mybool", "1"); 61 | $tk->AddParameterChar ( "both", 8, "time", "mytime", "09.45.29"); 62 | $tk->AddParameterChar ( "both", 26, "timestamp", "mystamp", "2011-12-29-12.45.29.000000"); 63 | $tk->AddParameterChar ( "both", 10, "date", "mydate", "2009-05-11"); 64 | new ProgramParameter ("3i0","both", "byte", "myint8", 0); // work around missing $tk->AddParameterInt8 65 | new ProgramParameter ("5i0","both", "short", "myint16", 0); // work around missing $tk->AddParameterInt16 66 | $tk->AddParameterInt32 ( "both", "int", "myint32", 0); 67 | $tk->AddParameterInt64 ( "both", "longlong", "myint64", 0); 68 | new ProgramParameter ("3u0","both", "ubyte", "myuint8", 0); // work around missing $tk->AddParameterUInt8 69 | new ProgramParameter ("5u0","both", "ushort", "myuint16", 0); // work around missing $tk->AddParameterUInt16 70 | $tk->AddParameterUInt32 ( "both", "uint", "myuint32", 0); 71 | $tk->AddParameterUInt64 ( "both", "ulonglong", "myuint64", 0); 72 | $tk->AddParameterChar ( "out", 1, "ind '0'/'1'" "myind", ""); // indicator type is boolean 1-byte character 73 | $tk->AddDataStruct ([array of parameters], "myds" ); 74 | 75 | ``` 76 | -------------------------------------------------------------------------------- /samples/options.php: -------------------------------------------------------------------------------- 1 | true | false 7 | 8 | // 'stateless' is the most important option. Although false is the default value, true is much better for most users. 9 | // 'stateless' => true is usually the better choice. XMLSERVICE will run in an existing database job without creating additional jobs. 10 | // 'stateless' => false (default) results in a stateful connection where XMLSERVICE launches a separate job that stays running until ended. 11 | // When using stateless => false, remember to choose an InternalKey. 12 | // Example of 'stateless' => true, which is best for most uses. 13 | $conn->setOptions(array('stateless' => true)); // specify true unless you have a good reason not to. 14 | 15 | // # 'InternalKey' => $artibitraryDirectory 16 | 17 | // 'InternalKey', combined with turning on stateful mode, will cause a stateful toolkit job to be identified uniquely by the system. 18 | // The value of InternalKey should be an empty directory, typically under /tmp. 19 | // Any toolkit script specifying this InternalKey value and stateful mode can access the same XMLSERVICE job later as needed. 20 | // The stateful technique is useful for accessing QTEMP and *LDA resources in this unique job. 21 | // The job will remain running until ended *IMMED or timed out using a timeout option (see below) 22 | // Note: omitting InternalKey in stateful mode would cause a default key of '/tmp/Toolkit' to be used, which would mean everyone is sharing a single XMLSERVICE job. 23 | $conn->setOptions(array('stateless' => false, 24 | 'InternalKey' => "/tmp/alan2" 25 | )); 26 | 27 | // # 'idleTimeout' => $idleTimeoutSeconds 28 | 29 | // 'idleTimeout' will cause a stateful toolkit job to end after the specified number of seconds of inactivity. 30 | // This option will tell XMLSERVICE to use timeout setting *idle(3600/kill). For advanced timeout options, see: http://yips.idevcloud.com/wiki/index.php/XMLService/XMLSERVICETIMEOUT 31 | 32 | // In the example below, the XMLSERVICE job will end after 3600 seconds (one hour) of not being used. 33 | $conn->setOptions(array('stateless' => false, 34 | 'InternalKey' => "/tmp/alan2", 35 | 'idleTimeout' => 3600 36 | )); 37 | 38 | // # 'customControl' => $customControlKeys 39 | 40 | // 'customControl' is useful when you need to send control keys supported by XMLSERVICE but that aren't directly exposed by the PHP Toolkit. 41 | // Helpful for the less popular control keys that you might still need from time to time. 42 | // To send multiple keys, include a space between each. 43 | 44 | // Example #1: *java when calling an RPG program that includes java. 45 | 46 | $conn->setOptions(array('stateless' => true, 47 | 'customControl'=>'*java' 48 | )); 49 | 50 | // Example #2: *call. Useful for setting a timeout on a hanging or looping RPG program. This example for stateless mode. 51 | $conn->setOptions(array('stateless' => true, 52 | 'customControl'=> '*call(15/kill/server)', // return control to PHP after waiting 15 seconds. CPF is available in error code as well. 53 | )); 54 | 55 | // Example #3: *call and *sbmjob. Useful for setting a timeout on a hanging or looping RPG program. This example for stateful (IPC) mode. 56 | $conn->setOptions(array('stateless' => false, // explicitly be stateful when using this timeout option 57 | 'internalKey' => '/tmp/tkitjob2', // arbitrary directory name under /tmp to represent unique job 58 | 'customControl'=> '*call(15/kill/server) *sbmjob', // return control to PHP after waiting 15 seconds. CPF is available in error code as well. 59 | )); 60 | 61 | 62 | // # 'debug' => true or false 63 | // # 'debugLogFile' => path of log file 64 | // For both, defaults are set in toolkit.ini under the [system] group. 65 | 66 | // The debug log, when enabled, logs all XML sent/received, as well as connection information, and timing of each request. 67 | // It is invaluable for troubleshooting performance and program calls. 68 | // Make sure the PHP user (probably QTMHHTTP if running from the web) has *RW access to the file, 69 | // and, if the file doesn't yet exist, *RWX access to the parent directory so PHP can create the file. 70 | // The file can get large, so we recommend setting debug => false when logging is not needed. 71 | 72 | $conn->setOptions(array('debug' => true, 73 | 'debugLogFile'=>'/my/path/tkit_debug.log' 74 | )); 75 | 76 | 77 | // sbmjobCommand is an advanced keyword. Use it when you need to set your own job name or other parameters for a stateful toolkit job. 78 | // # 'sbmjobCommand' => full sbmjob command for full control of stateful toolkit job parameters (advanced) 79 | 80 | // Set these three values according to your needs (sample values given here) 81 | $internalKey = '/tmp/mykey123'; // unique identifier for job 82 | $jobName = 'MYTOOLKIT3'; 83 | $jobQueue = 'QSYS/QSYSNOMAX'; 84 | 85 | // specify stateful mode and the internal key (IPC) we established earlier. 86 | $conn->setOptions(['stateless' => false, 87 | 'internalKey' => $internalKey, 88 | ]); 89 | 90 | // Set up values needed in the sbmjob command 91 | $ipc = trim($conn->getInternalKey()); 92 | $serviceLibrary = trim($conn->getOption('XMLServiceLib')); 93 | $jobNameParam = "JOB($jobName)"; 94 | $jobqParam = "JOBQ($jobQueue)"; 95 | 96 | // Fully customize where and how XMLSERVICE runs by building the SBMJOB command and set it up in the toolkit 97 | $sbmjobCommand = "SBMJOB CMD(CALL PGM({$serviceLibrary}/XMLSERVICE) PARM('$ipc')) {$jobNameParam} {$jobqParam}"; 98 | $conn->setOptions(['sbmjobCommand' => $sbmjobCommand, 99 | 'sbmjobParams' => '', // empty because set these values in sbmjobCommand 100 | 'customControl' => '*sbmjob', // required when setting sbmjobCommand 101 | ]); 102 | 103 | ?> 104 | -------------------------------------------------------------------------------- /ToolkitApi/Odbcsupp.php: -------------------------------------------------------------------------------- 1 | =') && $conn instanceof \Odbc\Connection)) { 42 | return $conn; 43 | } 44 | } 45 | 46 | $this->setError(); 47 | return false; 48 | } 49 | 50 | /** 51 | * @param resource $conn 52 | */ 53 | public function disconnect($conn) 54 | { 55 | if ((version_compare(PHP_VERSION, '8.4.0', '<') && is_resource($conn)) || 56 | (version_compare(PHP_VERSION, '8.4.0', '>=') && $conn instanceof \Odbc\Connection)) { 57 | odbc_close($conn); 58 | } 59 | } 60 | 61 | /** 62 | * set error code and message based on last odbc connection/prepare/execute error. 63 | * 64 | * @todo: consider using GET DIAGNOSTICS for even more message text: 65 | * http://publib.boulder.ibm.com/infocenter/iseries/v5r4/index.jsp?topic=%2Frzala%2Frzalafinder.htm 66 | * 67 | * @param resource|null $conn 68 | */ 69 | protected function setError($conn = null) 70 | { 71 | // is conn resource provided, or do we get last error? 72 | if ($conn) { 73 | $this->setErrorCode(odbc_error($conn)); 74 | $this->setErrorMsg(odbc_errormsg($conn)); 75 | } else { 76 | $this->setErrorCode(odbc_error()); 77 | $this->setErrorMsg(odbc_errormsg()); 78 | } 79 | } 80 | 81 | /** 82 | * @param string $errorCode 83 | */ 84 | protected function setErrorCode($errorCode) 85 | { 86 | $this->last_errorcode = $errorCode; 87 | } 88 | 89 | /** 90 | * @return string 91 | */ 92 | public function getErrorCode() 93 | { 94 | return $this->last_errorcode; 95 | } 96 | 97 | /** 98 | * @param string $errorMsg 99 | */ 100 | protected function setErrorMsg($errorMsg) 101 | { 102 | $this->last_errormsg = $errorMsg; 103 | } 104 | 105 | /** 106 | * @return string 107 | */ 108 | public function getErrorMsg() 109 | { 110 | return $this->last_errormsg; 111 | } 112 | 113 | /** 114 | * this function used for special stored procedure call only 115 | * 116 | * @param resource $conn 117 | * @param string $stmt 118 | * @param array $bindArray 119 | * @return string 120 | */ 121 | public function execXMLStoredProcedure($conn, $stmt, $bindArray) 122 | { 123 | $crsr = odbc_prepare($conn, $stmt); 124 | 125 | if (!$crsr) { 126 | $this->setError($conn); 127 | return false; 128 | } 129 | 130 | // extension problem: sends warning message into the php_log or stdout 131 | // about number of result sets. (switch on return code of SQLExecute() 132 | // SQL_SUCCESS_WITH_INFO 133 | if (!@odbc_execute($crsr , array($bindArray['internalKey'], $bindArray['controlKey'], $bindArray['inputXml']))) { 134 | $this->setError($conn); 135 | return "ODBC error code: " . $this->getErrorCode() . ' msg: ' . $this->getErrorMsg(); 136 | } 137 | 138 | // disconnect operation cause crush in fetch, nothing appears as sql script. 139 | $row=''; 140 | $outputXML = ''; 141 | if (!$bindArray['disconnect']) { 142 | while (odbc_fetch_row($crsr)) { 143 | $tmp = odbc_result($crsr, 1); 144 | 145 | if ($tmp) { 146 | // because of ODBC problem blob transferring should execute some "clean" on returned data 147 | if (strstr($tmp , "")) { 148 | $pos = strpos($tmp, ""); 149 | $pos += strlen(""); // @todo why append this value? 150 | $row .= substr($tmp, 0, $pos); 151 | break; 152 | } else { 153 | $row .= $tmp; 154 | } 155 | } 156 | } 157 | $outputXML = $row; 158 | } 159 | 160 | return $outputXML; 161 | } 162 | 163 | /** 164 | * @param resource $conn 165 | * @param string $stmt 166 | * @return array 167 | */ 168 | public function executeQuery($conn, $stmt) 169 | { 170 | $txt = array(); 171 | $crsr = odbc_exec($conn, $stmt); 172 | 173 | if ((version_compare(PHP_VERSION, '8.4.0', '<') && is_resource($crsr)) || 174 | (version_compare(PHP_VERSION, '8.4.0', '>=') && $crsr instanceof \Odbc\Result)) { 175 | while (odbc_fetch_row($crsr)) { 176 | $row = odbc_result($crsr, 1); 177 | 178 | if (!$row) { 179 | break; 180 | } 181 | 182 | $txt[]= $row; 183 | } 184 | } else { 185 | $this->setError($conn); 186 | } 187 | 188 | return $txt; 189 | } 190 | } 191 | -------------------------------------------------------------------------------- /ToolkitApi/toolkit.ini.zs5: -------------------------------------------------------------------------------- 1 | ; toolkit.ini for Zend/PHP Toolkit and Compatibility Wrapper (CW) 2 | ; with default values appropriate for Zend Server 5 3 | 4 | [system] 5 | ; set library where XTOOLKIT lives, most likely XMLSERVICE (testing) or ZENDSVR (production) 6 | XMLServiceLib = "ZENDSVR" ; production Zend Server 5.x 7 | ;XMLServiceLib = "ZENDSVR6" ; production Zend Server 6.x/7.x 8 | ;XMLServiceLib = "XMLSERVICE" ; for testing new XMLSERVICE packages 9 | 10 | ; Location of ZSXMLSRV "helper" service program. Overrides ToolkitServiceSet's ZSTOOLKITLIB constant 11 | HelperLib = "ZENDSVR" ; library of ZS "helper" service program. ZENDSVR or ZENDSVR6 12 | 13 | ; debug sets PHP toolkit's debug mode on or off (true/false). 14 | ; Default log file: /usr/local/zendsvr(6/7)/var/log/tkit_debug.log 15 | ; This log will grow large, so leave this false when you do not need to log everything. 16 | debug = false 17 | debugLogFile = "/usr/local/zendsvr/var/log/tkit_debug.log" 18 | 19 | ; Enable internal, low-level XMLSERVICE trace into table XMLSERVLOG/LOG 20 | ; (New in PHP tkit 1.3.1, requiring XMLSERVICE 1.7.1) 21 | ; default is false 22 | trace = false 23 | 24 | ; encoding (ISO-8859-1 is default. For some languages, such as Japanese, UTF-8 seems better.) 25 | encoding = "ISO-8859-1" 26 | ;encoding = "UTF-8" 27 | 28 | ; Advanced CCSID options. Use all three options together. 29 | ; Details under the heading "CCSID user override - xmlservice options (hex/before/after)": 30 | ; http://www.youngiprofessionals.com/wiki/index.php/XMLSERVICE/XMLSERVICECCSID 31 | ;ccsidBefore = "819/37" 32 | ;ccsidAfter = "37/819" 33 | ;useHex = true 34 | 35 | ; paseCcsid controls CCSID for type of functions such as WRKACTJOB ('system' command in PASE) 36 | ; Default is 819. Another practical value is 1208 (UTF-8). 37 | ;paseCcsid = 819 38 | 39 | ; Optionally provide submit options here 40 | ; Format: JOBDLIB/JOBD/JOBNAME. If not specified, will be ZENDSVR6/ZSVR_JOBD/XTOOLKIT 41 | ;sbmjob_params = "ZENDSVR/ZSVR_JOBD/XTOOLKIT" 42 | ; Or specify another sbmjob combination, such as QSYS/QSRVJOB/XTOOLKIT, 43 | ; if ZENDSVR library isn't present on the system or LPAR. 44 | ;sbmjob_params = "QSYS/QSRVJOB/XTOOLKIT" 45 | 46 | ; If you plan to connect to v5r4 IBM i systems from this toolkit installation, set v5r4=true. 47 | ; The toolkit will then optimize program calls for v5r4 (important when calling non-ILE programs on v5r4). 48 | ; This setting is equivalent to the following PHP code: $conn->setToolkitServiceParams(array('v5r4'=>true)); 49 | ; The default is false. 50 | v5r4 = false 51 | 52 | ; Data Structure integrity 53 | ; Whether to retain the tree-like hierarchy of data structures (dataStructureIntegrity = true) 54 | ; or to ignore DSes, flattening out all data elements to a single level (dataStructureIntegrity = false) 55 | ; Starting in Zend Server 6, the default here is true. 56 | ; For backward compatibility with pre-1.4.0 behavior, change one or both to false. 57 | dataStructureIntegrity = false 58 | ; Array Integrity 59 | ; Allow true array parameters with better functionality. 60 | ; For backward compatibility with pre-1.4.0, set to false. 61 | arrayIntegrity = false 62 | 63 | ; CW only: stateless mode is default for i5_connect (though automatically overridden if private conns are used) 64 | stateless = true 65 | 66 | [transport] 67 | ; transport type allows configuration of transport from this INI. 68 | ;transportType = "ibm_db2" ; ibm_db2 is default. Other choices: "odbc", "http" 69 | ; for http transport only 70 | ;httpTransportUrl = "http://example.com/cgi-bin/xmlcgi.pgm" 71 | ; default plug size, which is the expected output size. 4K, 32K, 512K (default), 65K, 512K, 1M, 5M, 10M, 15M 72 | ; can also change in code with $conn->setOptions(array('plugSize' => '4K')); or desired size 73 | ;plugSize = "512K" 74 | 75 | 76 | [log] 77 | ; Both CW and regular toolkit: warnings and errors will be written to the logfile (CW and regular toolkit). 78 | logfile = "/usr/local/zendsvr/var/log/toolkit.log" 79 | 80 | ; CW only: If logCwConnect = true then CW connection events will be written to the logfile. 81 | ; This log will grow large, so set logCwConnects=false in production. 82 | ; Certain warnings and errors will be written regardless. 83 | logCwConnect = false 84 | 85 | 86 | ; CW only: map hosts from ip/host to database names 87 | [hosts] 88 | ; map ip/host names to database names (WRKDBRDIRE) 89 | ; because old toolkit used ip/host name; Zend's toolkit uses database name. 90 | ; Two common keys are set by default. In CW, specify 'localhost' as host name if running on local IBM i. 91 | localhost = "*LOCAL" 92 | 127.0.0.1 = "*LOCAL" 93 | 94 | ; examples of other mappings 95 | ;1.2.3.4 = DB1 96 | ;myhost = MAINDB 97 | ;example.com = LPARDB 98 | 99 | 100 | 101 | [testing] 102 | ; parse_only means do not run your program. Only parse the XML and return debug info. Useful for testing dim/dou/counters 103 | ; This setting is equivalent to the following PHP code: $conn->setToolkitServiceParams(array('parseOnly'=>true)); 104 | ; The default is false. 105 | ;parse_only = false 106 | ; parse_debug_level determines the amount of parsing detail to be logged in debug log (1-9: 1=none, 9=all) 107 | ; This setting is equivalent to the following PHP code: $conn->setToolkitServiceParams(array('parseDebugLevel'=>1)); // any number 1-9 108 | ; The default is null. 109 | ;parse_debug_level = 1 110 | 111 | [cw] 112 | ; if want to close db connection on i5_close(); 113 | fullDbClose = false 114 | 115 | ; CW Demo configuration (may be deprecated in a future release) 116 | [demo] 117 | demo_library=CWDEMO 118 | 119 | ; For CW demo script, optional settings: initlibl, ccsid, jobname, idle_timeout, transport_type 120 | ; These affect the demo script only. 121 | ; May be helpful to specify QGPL if not ordinarily present because it's required in liblist to enable spool file access (Qshell's catsplf command used by the CW). 122 | ;initlibl = "QGPL" 123 | ;ccsid = 37 124 | ;jobname = PHPJOBX 125 | ;idle_timeout = 30 126 | ;transport_type = "odbc" ; ibm_db2 is default. Other choice is "odbc" 127 | 128 | ; for demo, use a persistent connection (true or false) 129 | persistent = true 130 | 131 | ; for demo, whether to use a private connection (true or false) 132 | private = false 133 | 134 | ; for demo script, if specified private above, this is the conn# to use. 135 | ; A value of 0 means the toolkit should generate the number. 136 | ; Recommendation: use 0 the first time to allow the toolkit to generate a number, 137 | ; then edit the INI file to specify the generated number, then run demo again. 138 | ; Private connections are slow the first time, then fast afterard. 139 | private_num = 0 -------------------------------------------------------------------------------- /ToolkitApi/SshSupp.php: -------------------------------------------------------------------------------- 1 | conn, $this->getXmlserviceCliPath()); 28 | if (!$ssh_stdio) { 29 | $this->setErrorCode("SSH2_EXEC"); 30 | $this->setErrorMsg("error executing command over SSH"); 31 | return false; 32 | } 33 | /* XXX */ 34 | stream_set_blocking($ssh_stdio, true); 35 | $written = fwrite($ssh_stdio, $xmlIn); 36 | if ($written === false) { 37 | $this->setErrorCode("SSH2_FWRITE"); 38 | $this->setErrorMsg("error writing to stdin"); 39 | return false; 40 | } 41 | fflush($ssh_stdio); 42 | ssh2_send_eof($ssh_stdio); 43 | $xmlOut = stream_get_contents($ssh_stdio); 44 | return $xmlOut; 45 | } 46 | 47 | /** 48 | * @return string 49 | */ 50 | public function getErrorCode() 51 | { 52 | return $this->last_errorcode; 53 | } 54 | 55 | /** 56 | * @return string 57 | */ 58 | public function getErrorMsg() 59 | { 60 | return $this->last_errormsg; 61 | } 62 | 63 | /** 64 | * @param $errorCode 65 | */ 66 | protected function setErrorCode($errorCode) 67 | { 68 | $this->last_errorcode = $errorCode; 69 | } 70 | 71 | /** 72 | * @param $errorMsg 73 | */ 74 | protected function setErrorMsg($errorMsg) 75 | { 76 | $this->last_errormsg = $errorMsg; 77 | } 78 | 79 | /** 80 | * @param string $path 81 | */ 82 | public function setXmlserviceCliPath($path) 83 | { 84 | $this->xmlserviceCliPath = $path; 85 | } 86 | 87 | /** 88 | * @return null 89 | */ 90 | public function getXmlserviceCliPath() 91 | { 92 | return $this->xmlserviceCliPath; 93 | } 94 | 95 | private function checkCompat() 96 | { 97 | // Only post-1.2 versions of ssh2 have ssh2_send_eof 98 | if (!extension_loaded("ssh2")) { 99 | $this->setErrorCode("SSH2_NOT_LOADED"); 100 | $this->setErrorMsg("the ssh2 extension isn't loaded"); 101 | return false; 102 | } 103 | if (!function_exists("ssh2_send_eof")) { 104 | $this->setErrorCode("SSH2_NO_SEND_EOF"); 105 | $this->setErrorMsg("the ssh2 extension is too old to support ssh2_send_eof, use 1.3 or newer"); 106 | return false; 107 | } 108 | return true; 109 | } 110 | 111 | /** 112 | * @param resource $conn 113 | * @return SshSupp|bool 114 | */ 115 | public function connectWithExistingConnection($conn) 116 | { 117 | if (!$this->checkCompat()) { 118 | return false; 119 | } 120 | if (!$conn || !is_resource($conn)) { 121 | $this->setErrorCode("SSH2_NOT_RESOURCE"); 122 | $this->setErrorMsg("connection isn't a valid resource"); 123 | return false; 124 | } 125 | $this->conn = $conn; 126 | return $this; 127 | } 128 | 129 | /** 130 | * @param string $server 131 | * @param $user 132 | * @param $password 133 | * @param array $options 134 | * @return SshSupp|bool 135 | */ 136 | public function connect($server, $user, $password, $options = array()) 137 | { 138 | if (!$this->checkCompat()) { 139 | return false; 140 | } 141 | // XXX: Set advanced methods here 142 | $port = array_key_exists("sshPort", $options) ? $options["sshPort"] : 22; 143 | $conn = ssh2_connect($server, $port); 144 | if (!$conn) { 145 | $this->setErrorCode("SSH2_CONNECT"); 146 | $this->setErrorMsg("error connecting to SSH"); 147 | return false; 148 | } 149 | // XXX: should probably be doing better than SHA1 here, but ssh2 150 | $remoteFP = ssh2_fingerprint($conn, SSH2_FINGERPRINT_SHA1 | SSH2_FINGERPRINT_HEX); 151 | $trustedFP = array_key_exists("sshFingerprint", $options) ? $options["sshFingerprint"] : false; 152 | if ($trustedFP !== false && $trustedFP !== $remoteFP) { 153 | $this->setErrorCode("SSH2_FINGERPRINT"); 154 | $this->setErrorMsg("the fingerprint ($remoteFP) differs from the set fingerprint"); 155 | ssh2_disconnect($conn); 156 | return false; 157 | } 158 | $authMethod = array_key_exists("sshMethod", $options) ? $options["sshMethod"] : "password"; 159 | switch ($authMethod) { 160 | case "keyfile": 161 | $pub = $options["sshPublicKeyFile"]; 162 | $priv = $options["sshPrivateKeyFile"]; 163 | $passphrase = $options["sshPrivateKeyPassphrase"]; 164 | if (!ssh2_auth_pubkey_file($conn, $user, $pub, $priv, $passphrase)) { 165 | $this->setErrorCode("SSH2_AUTH_KEYFILE"); 166 | $this->setErrorMsg("error performing keyfile auth over SSH"); 167 | ssh2_disconnect($conn); 168 | return false; 169 | } 170 | break; 171 | case "agent": 172 | if (!ssh2_auth_agent($conn, $user)) { 173 | $this->setErrorCode("SSH2_AUTH_AGENT"); 174 | $this->setErrorMsg("error performing agent auth over SSH"); 175 | ssh2_disconnect($conn); 176 | return false; 177 | } 178 | break; 179 | case "password": 180 | if (!ssh2_auth_password($conn, $user, $password)) { 181 | $this->setErrorCode("SSH2_AUTH_PASSWORD"); 182 | $this->setErrorMsg("error performing password auth over SSH"); 183 | ssh2_disconnect($conn); 184 | return false; 185 | } 186 | break; 187 | } 188 | 189 | $this->conn = $conn; 190 | 191 | return $this; 192 | } 193 | 194 | public function disconnect() 195 | { 196 | if (is_resource($this->conn)) { 197 | ssh2_disconnect($this->conn); 198 | } 199 | } 200 | } 201 | -------------------------------------------------------------------------------- /ToolkitApi/toolkit.ini.zs6: -------------------------------------------------------------------------------- 1 | ; toolkit.ini for Zend/PHP Toolkit and Compatibility Wrapper (CW) 2 | ; with default values appropriate for Zend Server 6/7 3 | ;comment 4 | [system] 5 | ; set library where XTOOLKIT lives, most likely XMLSERVICE (testing) or ZENDSVR (production) 6 | ;XMLServiceLib = "ZENDSVR" ; production Zend Server 5.x 7 | XMLServiceLib = "ZENDSVR6" ; production Zend Server 6.x/7.x 8 | ;XMLServiceLib = "XMLSERVICE" ; for testing new XMLSERVICE packages 9 | 10 | ; Location of ZSXMLSRV "helper" service program. Overrides ToolkitServiceSet's ZSTOOLKITLIB constant 11 | HelperLib = "ZENDSVR6" ; library of ZS "helper" service program. ZENDSVR or ZENDSVR6 12 | 13 | ; debug sets PHP toolkit's debug mode on or off (true/false). 14 | ; Default log file: /usr/local/zendsvr(6/7)/var/log/tkit_debug.log 15 | ; This log will grow large, so leave this false when you do not need to log everything. 16 | debug = false 17 | debugLogFile = "/usr/local/zendsvr6/var/log/tkit_debug.log" 18 | 19 | ; Enable internal, low-level XMLSERVICE trace into table XMLSERVLOG/LOG 20 | ; (New in PHP tkit 1.3.1, requiring XMLSERVICE 1.7.1) 21 | ; default is false 22 | trace = false 23 | 24 | ; encoding (ISO-8859-1 is default. For some languages, such as Japanese, UTF-8 seems better.) 25 | encoding = "ISO-8859-1" 26 | ;encoding = "UTF-8" 27 | 28 | ; Advanced CCSID options. Use all three options together. 29 | ; Details under the heading "CCSID user override - xmlservice options (hex/before/after)": 30 | ; http://www.youngiprofessionals.com/wiki/index.php/XMLSERVICE/XMLSERVICECCSID 31 | ;ccsidBefore = "819/37" 32 | ;ccsidAfter = "37/819" 33 | ;useHex = true 34 | 35 | ; paseCcsid controls CCSID for type of functions such as WRKACTJOB ('system' command in PASE) 36 | ; Default is 819. Another practical value is 1208 (UTF-8). 37 | ;paseCcsid = 819 38 | 39 | ; Optionally provide submit options here 40 | ; Format: JOBDLIB/JOBD/JOBNAME. If not specified, will be ZENDSVR6/ZSVR_JOBD/XTOOLKIT 41 | ;sbmjob_params = "ZENDSVR6/ZSVR_JOBD/XTOOLKIT" 42 | ; Or specify another sbmjob combination, such as QSYS/QSRVJOB/XTOOLKIT, 43 | ; if ZENDSVR6 library isn't present on the system or LPAR. 44 | ;sbmjob_params = "QSYS/QSRVJOB/XTOOLKIT" 45 | 46 | ; If you plan to connect to v5r4 IBM i systems from this toolkit installation, set v5r4=true. 47 | ; The toolkit will then optimize program calls for v5r4 (important when calling non-ILE programs on v5r4). 48 | ; This setting is equivalent to the following PHP code: $conn->setToolkitServiceParams(array('v5r4'=>true)); 49 | ; The default is false. 50 | v5r4 = false 51 | 52 | ; Data Structure integrity 53 | ; Whether to retain the tree-like hierarchy of data structures (dataStructureIntegrity = true) 54 | ; or to ignore DSes, flattening out all data elements to a single level (dataStructureIntegrity = false) 55 | ; Starting in Zend Server 6, the default here is true. 56 | ; For backward compatibility with pre-1.4.0 behavior, change one or both to false. 57 | dataStructureIntegrity = true 58 | ; Array Integrity 59 | ; Allow true array parameters with better functionality. 60 | ; For backward compatibility with pre-1.4.0, set to false. 61 | arrayIntegrity = true 62 | 63 | ; CW only: stateless mode is default for i5_connect (though automatically overridden if private conns are used) 64 | stateless = true 65 | 66 | [transport] 67 | ; transport type allows configuration of transport from this INI. 68 | ;transportType = "ibm_db2" ; ibm_db2 is default. Other choices: "odbc", "http", "https" 69 | ; for http transport only 70 | ;httpTransportUrl = "https://example.com/cgi-bin/xmlcgi.pgm" 71 | ;sslCaFile = "/path/to/cert.pem" 72 | ;serverName = "example.com" 73 | ; default plug size, which is the expected output size. 4K, 32K, 512K (default), 65K, 512K, 1M, 5M, 10M, 15M 74 | ; can also change in code with $conn->setOptions(array('plugSize' => '4K')); or desired size 75 | ;plugSize = "512K" 76 | 77 | 78 | [log] 79 | ; Both CW and regular toolkit: warnings and errors will be written to the logfile (CW and regular toolkit). 80 | logfile = "/usr/local/zendsvr6/var/log/toolkit.log" 81 | 82 | ; CW only: If logCwConnect = true then CW connection events will be written to the logfile. 83 | ; This log will grow large, so set logCwConnects=false in production. 84 | ; Certain warnings and errors will be written regardless. 85 | logCwConnect = false 86 | 87 | 88 | ; CW only: map hosts from ip/host to database names 89 | [hosts] 90 | ; map ip/host names to database names (WRKDBRDIRE) 91 | ; because old toolkit used ip/host name; Zend's toolkit uses database name. 92 | ; Two common keys are set by default. In CW, specify 'localhost' as host name if running on local IBM i. 93 | localhost = "*LOCAL" 94 | 127.0.0.1 = "*LOCAL" 95 | 96 | ; examples of other mappings 97 | ;1.2.3.4 = DB1 98 | ;myhost = MAINDB 99 | ;example.com = LPARDB 100 | 101 | 102 | 103 | [testing] 104 | ; parse_only means do not run your program. Only parse the XML and return debug info. Useful for testing dim/dou/counters 105 | ; This setting is equivalent to the following PHP code: $conn->setToolkitServiceParams(array('parseOnly'=>true)); 106 | ; The default is false. 107 | ;parse_only = false 108 | ; parse_debug_level determines the amount of parsing detail to be logged in debug log (1-9: 1=none, 9=all) 109 | ; This setting is equivalent to the following PHP code: $conn->setToolkitServiceParams(array('parseDebugLevel'=>1)); // any number 1-9 110 | ; The default is null. 111 | ;parse_debug_level = 1 112 | 113 | [cw] 114 | ; if want to close db connection on i5_close(); 115 | fullDbClose = false 116 | 117 | ; CW Demo configuration (may be deprecated in a future release) 118 | [demo] 119 | demo_library=CWDEMO 120 | 121 | ; For CW demo script, optional settings: initlibl, ccsid, jobname, idle_timeout, transport_type 122 | ; These affect the demo script only. 123 | ; May be helpful to specify QGPL if not ordinarily present because it's required in liblist to enable spool file access (Qshell's catsplf command used by the CW). 124 | ;initlibl = "QGPL" 125 | ;ccsid = 37 126 | ;jobname = PHPJOBX 127 | ;idle_timeout = 30 128 | ;transport_type = "odbc" ; ibm_db2 is default. Other choice is "odbc" 129 | 130 | ; for demo, use a persistent connection (true or false) 131 | persistent = true 132 | 133 | ; for demo, whether to use a private connection (true or false) 134 | private = false 135 | 136 | ; for demo script, if specified private above, this is the conn# to use. 137 | ; A value of 0 means the toolkit should generate the number. 138 | ; Recommendation: use 0 the first time to allow the toolkit to generate a number, 139 | ; then edit the INI file to specify the generated number, then run demo again. 140 | ; Private connections are slow the first time, then fast afterard. 141 | private_num = 0 -------------------------------------------------------------------------------- /ToolkitApi/CW/ToolkitServiceCw.php: -------------------------------------------------------------------------------- 1 | conn)) 35 | { 36 | self::$instance->disconnect(); 37 | } 38 | 39 | // if we're forcing a new instance, or an instance hasn't been created yet, create one 40 | if ($forceNew || self::$instance == NULL) { 41 | $toolkitService = __CLASS__; 42 | self::$instance = new $toolkitService($databaseNameOrResource, $userOrI5NamingFlag, $password, $extensionPrefix, $isPersistent); 43 | } 44 | 45 | if (self::$instance) { 46 | // instance exists 47 | return self::$instance; 48 | } else { 49 | // some problem 50 | return false; 51 | } 52 | } 53 | 54 | /** 55 | * Return true if an instance of this object has already been created. 56 | * Return false if no instance has been instantiated. 57 | * 58 | * Same as the method in ToolkitApi\Toolkit. 59 | * Cwclasses has its own instance variable so we need this method here, too. 60 | * 61 | * Useful when users need to know if a "toolkit connection" has already been made. 62 | * Usage: 63 | * $isConnected = Toolkit::hasInstance(); 64 | * 65 | * @return boolean 66 | */ 67 | public static function hasInstance() 68 | { 69 | if (isset(self::$instance) && is_object(self::$instance)) { 70 | return true; 71 | } else { 72 | return false; 73 | } 74 | } 75 | 76 | /** 77 | * @param $num 78 | */ 79 | public function setPrivateConnNum($num) 80 | { 81 | $this->_privateConnNum = $num; 82 | } 83 | 84 | /** 85 | * @return null 86 | */ 87 | public function getPrivateConnNum() 88 | { 89 | return $this->_privateConnNum; 90 | } 91 | 92 | /** 93 | * establish whether the connection is new or not. Used by i5_get_property() 94 | * 95 | * @param bool $isNew 96 | */ 97 | public function setIsNewConn($isNew = true) 98 | { 99 | $this->_isNewConn = $isNew; 100 | } 101 | 102 | /** 103 | * @return bool 104 | */ 105 | public function isNewConn() 106 | { 107 | return $this->_isNewConn; 108 | } 109 | 110 | /** 111 | * when script ends, non-persistent connection should close 112 | */ 113 | public function __destruct() 114 | { 115 | /* call to disconnect() function to down connection */ 116 | $disconnect = false; 117 | 118 | // CW only: if connection is in a separate job and nonpersistent, end job (mimicking behavior of old toolkit) 119 | if (!$this->isStateless() && !$this->getIsPersistent()) { 120 | $disconnect = true; 121 | } 122 | 123 | if ($disconnect) { 124 | $this->disconnect(); 125 | } 126 | 127 | // parent destruct clears the object 128 | // parent::__destruct(); 129 | 130 | // need to clear extended cwclasses instance as well. 131 | if ($disconnect) { 132 | self::$instance = null; 133 | } 134 | } 135 | 136 | /** 137 | * Get the most recent system error code, if available. 138 | * TODO this may not work because CPFs are done at a class level (data areas etc.) 139 | */ 140 | public function getCPFErr() 141 | { 142 | // TODO get from Verify_CPFError or the other one 143 | return $this->CPFErr; 144 | } 145 | 146 | /** 147 | * After calling a program or command, we can export output as variables. 148 | * This method creates an array that can later be extracted into variables. 149 | * param array $outputDesc Format of output params 'CODE'=>'CODEvar' where the value becomes a PHP var name 150 | * param array $outputValues Optional. Array of output values to export 151 | * 152 | * @param array $outputDesc 153 | * @param array $outputValues 154 | * @return boolean true on success, false on some error 155 | */ 156 | public function setOutputVarsToExport(array $outputDesc, array $outputValues) 157 | { 158 | // for each piece of output, export it according to var name given in $outputDesc. 159 | if ($outputValues && is_array($outputValues) && count($outputValues)) { 160 | // initialize 161 | $this->_outputVarsToExport = array(); 162 | 163 | foreach ($outputValues as $paramName=>$value) { 164 | if (isset($outputDesc[$paramName])) { 165 | $variableNameToExport = $outputDesc[$paramName]; 166 | // create the global variable named by $ varName. 167 | 168 | $GLOBALS[$variableNameToExport] = $value; 169 | 170 | $this->_outputVarsToExport[$variableNameToExport] = $value; 171 | } 172 | } 173 | } 174 | 175 | return true; 176 | } 177 | 178 | /** 179 | * @return array 180 | */ 181 | public function getOutputVarsToExport() 182 | { 183 | return $this->_outputVarsToExport; 184 | } 185 | 186 | /** 187 | * pass in array of job attributes => values to update in the current job. 188 | * returns true on success, false on failure (failure probably means lack of authority). 189 | * 190 | * @param array $attrs 191 | * @return bool 192 | */ 193 | public function changeJob(array $attrs) 194 | { 195 | $cmdString = 'CHGJOB'; 196 | $success = i5_command($cmdString, $attrs); 197 | return $success; 198 | } 199 | } -------------------------------------------------------------------------------- /ToolkitApi/toolkit.ini: -------------------------------------------------------------------------------- 1 | ; toolkit.ini for Zend/PHP Toolkit and Compatibility Wrapper (CW) 2 | ; with default values appropriate for Zend Server 9 3 | ;comment 4 | [system] 5 | ; set library where XMLSERVICE objects live 6 | XMLServiceLib = "QXMLSERV" ; Library for XMLSERVICE shipped by IBM 7 | ;XMLServiceLib = "ZENDPHP7" ; production Zend Server 9.x 8 | 9 | ; Location of ZSXMLSRV "helper" service program. Overrides ToolkitServiceSet's ZSTOOLKITLIB constant 10 | HelperLib = "ZENDPHP7" ; library of ZS "helper" service program. ZENDSVR, ZENDSVR6, or ZENDPHP7 11 | 12 | ; debug sets PHP toolkit's debug mode on or off (true/false). 13 | ; Default log file: /usr/local/zendphp7/var/log/tkit_debug.log 14 | ; This log will grow large, so leave this false when you do not need to log everything. 15 | debug = false 16 | debugLogFile = "/usr/local/zendphp7/var/log/tkit_debug.log" 17 | 18 | ; Enable internal, low-level XMLSERVICE trace into table XMLSERVLOG/LOG 19 | ; (New in PHP tkit 1.3.1, requiring XMLSERVICE 1.7.1) 20 | ; default is false 21 | trace = false 22 | 23 | ; encoding (ISO-8859-1 is default. For some languages, such as Japanese, UTF-8 seems better.) 24 | encoding = "ISO-8859-1" 25 | ;encoding = "UTF-8" 26 | 27 | ; Advanced CCSID options. Use all three options together. 28 | ; Details under the heading "CCSID user override - xmlservice options (hex/before/after)": 29 | ; http://www.youngiprofessionals.com/wiki/index.php/XMLSERVICE/XMLSERVICECCSID 30 | ;ccsidBefore = "819/37" 31 | ;ccsidAfter = "37/819" 32 | ;useHex = true 33 | 34 | ; paseCcsid controls CCSID for type of functions such as WRKACTJOB ('system' command in PASE) 35 | ; Default is 819. Another practical value is 1208 (UTF-8). 36 | ;paseCcsid = 819 37 | 38 | ; Optionally provide submit options here 39 | ; Format: JOBDLIB/JOBD/JOBNAME. If not specified, will be ZENDSVR6/ZSVR_JOBD/XTOOLKIT 40 | ;sbmjob_params = "ZENDSVR6/ZSVR_JOBD/XTOOLKIT" 41 | ; Or specify another sbmjob combination, such as QSYS/QSRVJOB/XTOOLKIT, 42 | ; if ZENDSVR6 library isn't present on the system or LPAR. 43 | ;sbmjob_params = "QSYS/QSRVJOB/XTOOLKIT" 44 | 45 | ; If you plan to connect to v5r4 IBM i systems from this toolkit installation, set v5r4=true. 46 | ; The toolkit will then optimize program calls for v5r4 (important when calling non-ILE programs on v5r4). 47 | ; This setting is equivalent to the following PHP code: $conn->setToolkitServiceParams(array('v5r4'=>true)); 48 | ; The default is false. 49 | v5r4 = false 50 | 51 | ; Data Structure integrity 52 | ; Whether to retain the tree-like hierarchy of data structures (dataStructureIntegrity = true) 53 | ; or to ignore DSes, flattening out all data elements to a single level (dataStructureIntegrity = false) 54 | ; Starting in Zend Server 6, the default here is true. 55 | ; For backward compatibility with pre-1.4.0 behavior, change one or both to false. 56 | dataStructureIntegrity = true 57 | ; Array Integrity 58 | ; Allow true array parameters with better functionality. 59 | ; For backward compatibility with pre-1.4.0, set to false. 60 | arrayIntegrity = true 61 | 62 | ; CW only: stateless mode is default for i5_connect (though automatically overridden if private conns are used) 63 | stateless = true 64 | 65 | [transport] 66 | ; transport type allows configuration of transport from this INI. 67 | ;transportType = "ibm_db2" ; ibm_db2 is default. Other choices: "odbc", "http", "https" 68 | ; for http transport only 69 | ;httpTransportUrl = "https://example.com/cgi-bin/xmlcgi.pgm" 70 | ;sslCaFile = "/path/to/cert.pem" 71 | ;serverName = "example.com" 72 | ; default plug size, which is the expected output size. 4K, 32K, 512K (default), 65K, 512K, 1M, 5M, 10M, 15M 73 | ; can also change in code with $conn->setOptions(array('plugSize' => '4K')); or desired size 74 | ;plugSize = "512K" 75 | ; Override the binary called in the local and SSH transports. 76 | ; Useful for shimming the binary, or running it elsewhere. 77 | ;xmlserviceCliPath = "/QOpenSys/pkgs/bin/xmlservice-cli" 78 | 79 | 80 | [log] 81 | ; Both CW and regular toolkit: warnings and errors will be written to the logfile (CW and regular toolkit). 82 | logfile = "/usr/local/zendphp7/var/log/toolkit.log" 83 | 84 | ; CW only: If logCwConnect = true then CW connection events will be written to the logfile. 85 | ; This log will grow large, so set logCwConnects=false in production. 86 | ; Certain warnings and errors will be written regardless. 87 | logCwConnect = false 88 | 89 | 90 | ; CW only: map hosts from ip/host to database names 91 | [hosts] 92 | ; map ip/host names to database names (WRKDBRDIRE) 93 | ; because old toolkit used ip/host name; Zend's toolkit uses database name. 94 | ; Two common keys are set by default. In CW, specify 'localhost' as host name if running on local IBM i. 95 | localhost = "*LOCAL" 96 | 127.0.0.1 = "*LOCAL" 97 | 98 | ; examples of other mappings 99 | ;1.2.3.4 = DB1 100 | ;myhost = MAINDB 101 | ;example.com = LPARDB 102 | 103 | 104 | 105 | [testing] 106 | ; parse_only means do not run your program. Only parse the XML and return debug info. Useful for testing dim/dou/counters 107 | ; This setting is equivalent to the following PHP code: $conn->setToolkitServiceParams(array('parseOnly'=>true)); 108 | ; The default is false. 109 | ;parse_only = false 110 | ; parse_debug_level determines the amount of parsing detail to be logged in debug log (1-9: 1=none, 9=all) 111 | ; This setting is equivalent to the following PHP code: $conn->setToolkitServiceParams(array('parseDebugLevel'=>1)); // any number 1-9 112 | ; The default is null. 113 | ;parse_debug_level = 1 114 | 115 | [cw] 116 | ; if want to close db connection on i5_close(); 117 | fullDbClose = false 118 | 119 | ; CW Demo configuration (may be deprecated in a future release) 120 | [demo] 121 | demo_library=CWDEMO 122 | 123 | ; For CW demo script, optional settings: initlibl, ccsid, jobname, idle_timeout, transport_type 124 | ; These affect the demo script only. 125 | ; May be helpful to specify QGPL if not ordinarily present because it's required in liblist to enable spool file access (Qshell's catsplf command used by the CW). 126 | ;initlibl = "QGPL" 127 | ;ccsid = 37 128 | ;jobname = PHPJOBX 129 | ;idle_timeout = 30 130 | ;transport_type = "odbc" ; ibm_db2 is default. Other choice is "odbc" 131 | 132 | ; for demo, use a persistent connection (true or false) 133 | persistent = true 134 | 135 | ; for demo, whether to use a private connection (true or false) 136 | private = false 137 | 138 | ; for demo script, if specified private above, this is the conn# to use. 139 | ; A value of 0 means the toolkit should generate the number. 140 | ; Recommendation: use 0 the first time to allow the toolkit to generate a number, 141 | ; then edit the INI file to specify the generated number, then run demo again. 142 | ; Private connections are slow the first time, then fast afterard. 143 | private_num = 0 144 | -------------------------------------------------------------------------------- /ToolkitApi/SpooledFiles.php: -------------------------------------------------------------------------------- 1 | ToolkitSrvObj = $ToolkitSrvObj; 26 | 27 | // @todo do not assume a specific plug size. 28 | $this->ToolkitSrvObj->setOptions(array('plugSize'=>'1M')); 29 | 30 | $this->TMPFName = $this->ToolkitSrvObj->generate_name(); 31 | 32 | //do not use a QTEMP as temporary library. [not sure why not--A.S.] 33 | if ($UserLib != NULL && strcmp($UserLib, "QGPL")) { 34 | $this->TmpLib = $UserLib; 35 | } 36 | 37 | return $this; 38 | } else { 39 | return false; 40 | } 41 | } 42 | 43 | /** 44 | * 45 | */ 46 | public function __destruct() 47 | { 48 | // empty 49 | } 50 | 51 | /** 52 | * @return mixed 53 | */ 54 | public function getError() 55 | { 56 | return $this->ErrMessage; 57 | } 58 | 59 | /** 60 | * @param string $UserName 61 | * @return array|bool|null 62 | * @throws \Exception 63 | */ 64 | public function GetSPLList($UserName = "*CURRENT") 65 | { 66 | $list = NULL; 67 | $this->clearError(); 68 | 69 | $this->TmpUserSpace = new TmpUserSpace($this->ToolkitSrvObj, DFTLIB); 70 | 71 | $UsFullName = $this->TmpUserSpace->getUSFullName(); 72 | $params[] = $this->ToolkitSrvObj->AddParameterChar('in', 20, "User Space Name", 'userspacename', $UsFullName); 73 | $params[] = $this->ToolkitSrvObj->AddParameterChar('in', 10, "User Name", 'username', $UserName); 74 | //$retval[]= $this->ToolkitSrvObj->AddReturnParameter('10i0', 'retval', 0); 75 | $retval[]= $this->ToolkitSrvObj->AddParameterInt32('out', 'retval', 'retval', 0); 76 | 77 | $this->ToolkitSrvObj->PgmCall(ZSTOOLKITPGM, $this->ToolkitSrvObj->getOption('HelperLib'), 78 | $params, $retval, 79 | array ('func' => 'LISTSPLF' ) ); 80 | 81 | if (!$this->ToolkitSrvObj->isError()) { 82 | sleep(1); 83 | $readlen = $this->TmpUserSpace->RetrieveUserSpaceSize(); 84 | $SpoolFilesList = $this->TmpUserSpace->ReadUserSpace(1, $readlen); 85 | $this->TmpUserSpace->DeleteUserSpace(); 86 | 87 | if (trim($SpoolFilesList) > 0) 88 | { 89 | $list = $this->buildSpoolList(str_split($SpoolFilesList, $this->SPOOLFILELIST_SIZE)); 90 | return $list; 91 | } else { 92 | $this->setError("No spooled files found for ".$UserName); 93 | return NULL; //no spollfiles! 94 | } 95 | } else { 96 | $this->setError($this->ToolkitSrvObj->getLastError()); 97 | } 98 | 99 | unset($this->TmpUserSpace); 100 | return false; 101 | } 102 | 103 | /** 104 | * @param $SpoolInfListArr 105 | * @return array|null 106 | */ 107 | private function buildSpoolList($SpoolInfListArr ) 108 | { 109 | $list= NULL; 110 | 111 | foreach($SpoolInfListArr as $tmparr) 112 | { 113 | $list[] = array('Number' => substr($tmparr, 0, 4), 114 | 'Name' => substr($tmparr, 4, 10), 115 | 'JobNumber' => substr($tmparr, 14, 6), 116 | 'JobName' => substr($tmparr, 20,10), 117 | 'JobUser' => substr($tmparr, 30, 10), 118 | 'UserData' => substr($tmparr, 40, 10), 119 | 'QueueName' => substr($tmparr, 50, 20), 120 | 'TotalPages' => substr($tmparr, 70, 5), 121 | 'Status' => substr($tmparr, 75, 10), 122 | 'DateOpen'=> substr($tmparr, 85, 7), 123 | 'TimeOpen'=>substr($tmparr, 92, 6) 124 | ); 125 | } 126 | 127 | return $list; 128 | } 129 | 130 | /** 131 | * multi user using! 132 | * 133 | * @param $SplfName 134 | * @param $SplfNbr 135 | * @param $JobNmbr 136 | * @param $JobName 137 | * @param $JobUser 138 | * @param string $TMPFName 139 | * @return mixed|string 140 | */ 141 | public function GetSPLF($SplfName , $SplfNbr, $JobNmbr, $JobName, $JobUser, $TMPFName='') 142 | { 143 | $this->clearError(); 144 | if ($TMPFName != '') { 145 | $this->TMPFName = $TMPFName; 146 | } 147 | 148 | // @todo under the flag for current Object??? 149 | $crtf = "CRTDUPOBJ OBJ(ZSF255) FROMLIB(" . $this->ToolkitSrvObj->getOption('HelperLib') . ") OBJTYPE(*FILE) TOLIB($this->TmpLib) NEWOBJ($this->TMPFName)"; 150 | $this->ToolkitSrvObj->ClCommandWithCpf($crtf); // was ClCommand 151 | 152 | // clear the temp file 153 | $clrpfm = "CLRPFM $this->TmpLib/$this->TMPFName"; 154 | $this->ToolkitSrvObj->ClCommandWithCpf($clrpfm); // these all were CLCommand but we need CPFs. 155 | 156 | // copy spooled file to temp file 157 | $cmd = sprintf ("CPYSPLF FILE(%s) TOFILE($this->TmpLib/$this->TMPFName) JOB(%s/%s/%s) SPLNBR(%s)", 158 | $SplfName, trim($JobNmbr), trim($JobUser), trim($JobName), $SplfNbr); 159 | 160 | $this->ToolkitSrvObj->ClCommandWithCpf($cmd); 161 | sleep(1); 162 | // read the data from temp file 163 | $Txt = $this->ReadSPLFData(); 164 | 165 | // delete temp file 166 | $dltf = "DLTF FILE($this->TmpLib/$this->TMPFName)"; 167 | $this->ToolkitSrvObj->ClCommandWithCpf($dltf); 168 | 169 | return $Txt; 170 | } 171 | 172 | /** 173 | * @return mixed|string 174 | * @throws \Exception 175 | */ 176 | private function ReadSPLFData() 177 | { 178 | $Txt=''; 179 | $schemaSep = $this->ToolkitSrvObj->getOption('schemaSep'); // . or / 180 | $stmt = "SELECT ZSF255 FROM {$this->TmpLib}{$schemaSep}{$this->TMPFName} FOR FETCH ONLY"; 181 | 182 | try { 183 | $Txt = $this->ToolkitSrvObj->executeQuery($stmt); 184 | } catch(Exception $e) { 185 | $this->setError("ReadSPLFData() error:" . $e->getMessage()); 186 | } 187 | 188 | return $Txt; 189 | } 190 | 191 | /** 192 | * @param $msg 193 | */ 194 | private function setError($msg) 195 | { 196 | $this->ErrMessage = $msg; 197 | } 198 | 199 | /** 200 | * 201 | */ 202 | private function clearError() 203 | { 204 | $this->ErrMessage = ''; 205 | } 206 | } 207 | -------------------------------------------------------------------------------- /ToolkitApi/Db2supp.php: -------------------------------------------------------------------------------- 1 | setErrorCode('08001'); 34 | $this->setErrorMsg('Authorization failure on distributed database connection attempt. SQLCODE=-30082'); 35 | 36 | return false; 37 | } 38 | 39 | if ($options) { 40 | $driver_options = array(); 41 | 42 | // Test for existence of driver options 43 | if (array_key_exists('driver_options', $options)) { 44 | $driver_options = $options['driver_options'] ?: array(); 45 | } 46 | 47 | if ((isset($options['persistent'])) && $options['persistent']) { 48 | $conn = db2_pconnect($database, $user, $password, $driver_options); 49 | } else { 50 | $conn = db2_connect($database, $user, $password, $driver_options); 51 | } 52 | 53 | if (is_resource($conn)) { 54 | return $conn; 55 | } 56 | } 57 | 58 | $this->setErrorCode(db2_conn_error()); 59 | $this->setErrorMsg(db2_conn_errormsg()); 60 | 61 | return false; 62 | } 63 | 64 | /** 65 | * @param $conn 66 | */ 67 | public function disconnect($conn) 68 | { 69 | if (is_resource($conn)) { 70 | db2_close($conn); 71 | } 72 | } 73 | 74 | /** 75 | * disconnect, truly close, a persistent connection. 76 | * 77 | * NOTE: Only available on i5/OS 78 | * 79 | * @param $conn 80 | */ 81 | public function disconnectPersistent($conn) 82 | { 83 | if (is_resource($conn)) { 84 | db2_pclose($conn); 85 | } 86 | } 87 | 88 | /** 89 | * @return string 90 | */ 91 | public function getErrorCode() 92 | { 93 | return $this->last_errorcode; 94 | } 95 | 96 | /** 97 | * @return string 98 | */ 99 | public function getErrorMsg() 100 | { 101 | return $this->last_errormsg; 102 | } 103 | 104 | /** 105 | * set error code and message based on last db2 prepare or execute error. 106 | * 107 | * @todo: consider using GET DIAGNOSTICS for even more message text: 108 | * http://publib.boulder.ibm.com/infocenter/iseries/v5r4/index.jsp?topic=%2Frzala%2Frzalafinder.htm 109 | * 110 | * @param null $stmt 111 | */ 112 | protected function setStmtError($stmt = null) 113 | { 114 | if ($stmt) { 115 | $this->setErrorCode(db2_stmt_error($stmt)); 116 | $this->setErrorMsg(db2_stmt_errormsg($stmt)); 117 | } else { 118 | $this->setErrorCode(db2_stmt_error()); 119 | $this->setErrorMsg(db2_stmt_errormsg()); 120 | } 121 | } 122 | 123 | /** 124 | * @param $errorCode 125 | */ 126 | protected function setErrorCode($errorCode) 127 | { 128 | $this->last_errorcode = $errorCode; 129 | } 130 | 131 | /** 132 | * @param $errorMsg 133 | */ 134 | protected function setErrorMsg($errorMsg) 135 | { 136 | $this->last_errormsg = $errorMsg; 137 | } 138 | 139 | /** 140 | * this function used for special stored procedure call only 141 | * 142 | * @param $conn 143 | * @param $sql 144 | * @return bool 145 | */ 146 | public function execXMLStoredProcedure($conn, $sql, $bindArray) 147 | { 148 | 149 | $internalKey = $bindArray['internalKey']; 150 | $controlKey = $bindArray['controlKey']; 151 | $inputXml = $bindArray['inputXml']; 152 | $outputXml = $bindArray['outputXml']; 153 | 154 | // @todo see why error doesn't properly bubble up to top level. 155 | $crsr = @db2_prepare($conn, $sql); 156 | if (!$crsr) { 157 | $this->setStmtError(); 158 | return false; 159 | } 160 | 161 | // stored procedure takes four parameters. Each 'name' will be bound to a real PHP variable 162 | $params = array( 163 | array('position' => 1, 'name' => "internalKey", 'inout' => DB2_PARAM_IN), 164 | array('position' => 2, 'name' => "controlKey", 'inout' => DB2_PARAM_IN), 165 | array('position' => 3, 'name' => "inputXml", 'inout' => DB2_PARAM_IN), 166 | array('position' => 4, 'name' => "outputXml", 'inout' => DB2_PARAM_OUT), 167 | ); 168 | 169 | // bind the four parameters 170 | foreach ($params as $param) { 171 | $ret = db2_bind_param ($crsr, $param['position'], $param['name'], $param['inout']); 172 | if (!$ret) { 173 | // unable to bind a param. Set error and exit 174 | $this->setStmtError($crsr); 175 | return false; 176 | } 177 | } 178 | 179 | $ret = @db2_execute($crsr); 180 | if (!$ret) { 181 | // execution of XMLSERVICE stored procedure failed. 182 | $this->setStmtError($crsr); 183 | return false; 184 | } 185 | 186 | return $outputXml; 187 | } 188 | 189 | /** 190 | * returns a first column from sql stmt result set 191 | * 192 | * used in one place: iToolkitService's ReadSPLFData(). 193 | * 194 | * @todo eliminate this method if possible. 195 | * 196 | * @param $conn 197 | * @param $sql 198 | * @throws \Exception 199 | * @return array 200 | */ 201 | public function executeQuery($conn, $sql) 202 | { 203 | $txt = array(); 204 | $stmt = db2_exec($conn, $sql, array('cursor' => DB2_SCROLLABLE)); 205 | if (is_resource($stmt)) { 206 | while (db2_fetch_row($stmt)) { 207 | $column = db2_result($stmt, 0); 208 | $txt[] = $column; 209 | } 210 | } else { 211 | $this->setStmtError(); 212 | Throw new \Exception("Failure executing SQL: ($sql) " . db2_stmt_errormsg(), db2_stmt_error()); 213 | } 214 | 215 | return $txt; 216 | } 217 | } 218 | -------------------------------------------------------------------------------- /tests/ToolkitApiTest/ToolkitTest.php: -------------------------------------------------------------------------------- 1 | toolkit = new Toolkit('*LOCAL', '0', 'testPwd', 'http', false); 41 | } 42 | 43 | public function testCanAddSigned8ByteIntegerParameter(): void 44 | { 45 | $parameter = $this->toolkit->AddParameterInt8('both', 'test comment', 'testVar', 8); 46 | 47 | $this->assertTrue($parameter instanceof Int8Param); 48 | } 49 | 50 | public function testCanAddSigned16ByteIntegerParameter(): void 51 | { 52 | $parameter = $this->toolkit->AddParameterInt16('both', 'test comment', 'testVar', 10); 53 | 54 | $this->assertTrue($parameter instanceof Int16Param); 55 | } 56 | 57 | public function testCanAddSigned32ByteIntegerParameter(): void 58 | { 59 | $parameter = $this->toolkit->AddParameterInt32('both', 'test comment', 'testVar', 100); 60 | 61 | $this->assertTrue($parameter instanceof Int32Param); 62 | } 63 | 64 | public function testCanAddSigned64ByteIntegerParameter(): void 65 | { 66 | $parameter = $this->toolkit->AddParameterInt64('both', 'test comment', 'testVar', 1000); 67 | 68 | $this->assertTrue($parameter instanceof Int64Param); 69 | } 70 | 71 | public function testCanAddUnsigned8ByteIntegerParameter(): void 72 | { 73 | $parameter = $this->toolkit->AddParameterUInt8('both', 'test comment', 'testVar', 8); 74 | 75 | $this->assertTrue($parameter instanceof UInt8Param); 76 | } 77 | 78 | public function testCanAddUnsigned16ByteIntegerParameter(): void 79 | { 80 | $parameter = $this->toolkit->AddParameterUInt16('both', 'test comment', 'testVar', 8); 81 | 82 | $this->assertTrue($parameter instanceof UInt16Param); 83 | } 84 | 85 | public function testCanAddUnsigned32ByteIntegerParameter(): void 86 | { 87 | $parameter = $this->toolkit->AddParameterUInt32('both', 'test comment', 'testVar', 8); 88 | 89 | $this->assertTrue($parameter instanceof UInt32Param); 90 | } 91 | 92 | public function testCanAddUnsigned64ByteIntegerParameter(): void 93 | { 94 | $parameter = $this->toolkit->AddParameterUInt64('both', 'test comment', 'testVar', 8); 95 | 96 | $this->assertTrue($parameter instanceof UInt64Param); 97 | } 98 | 99 | public function testCanAddCharacterParameter(): void 100 | { 101 | $parameter = $this->toolkit->AddParameterChar('both', 10, 'CODE', 'CODE', 'code'); 102 | 103 | $this->assertTrue($parameter instanceof CharParam); 104 | } 105 | 106 | public function testCanAddFloatParameter(): void 107 | { 108 | $parameter = $this->toolkit->AddParameterFloat('both', 'test comment', 'varName', 'false'); 109 | 110 | $this->assertTrue($parameter instanceof FloatParam); 111 | } 112 | 113 | public function testCanAddRealParameter(): void 114 | { 115 | $parameter = $this->toolkit->AddParameterReal('both', 'test comment', 'varName', 'testValue'); 116 | 117 | $this->assertTrue($parameter instanceof RealParam); 118 | } 119 | 120 | public function testCanAddPackedDecimalParameter(): void 121 | { 122 | $parameter = $this->toolkit->AddParameterPackDec('both', 7,4, 'INDEC1', 'var3', '001.0001'); 123 | 124 | $this->assertTrue($parameter instanceof PackedDecParam); 125 | } 126 | 127 | public function testCanAddZonedParameter(): void 128 | { 129 | $parameter = $this->toolkit->AddParameterZoned('both', 12, 2, 'Check amount', 'amount', '2000.25'); 130 | 131 | $this->assertTrue($parameter instanceof ZonedParam); 132 | } 133 | 134 | public function testCanAddParameterHole(): void 135 | { 136 | $parameter = $this->toolkit->AddParameterHole(12, 'hole'); 137 | 138 | $this->assertTrue($parameter instanceof HoleParam); 139 | } 140 | 141 | public function testCanAddBinaryParameter(): void 142 | { 143 | $parameter = $this->toolkit->AddParameterBin('both', 20, 'UncodeSample', 'p1', 'test'); 144 | 145 | $this->assertTrue($parameter instanceof BinParam); 146 | } 147 | 148 | public function testCanAddParameterSize(): void 149 | { 150 | $size = $this->toolkit->AddParameterSize('test comment', 'varName', 3); 151 | 152 | $this->assertTrue($size instanceof SizeParam); 153 | } 154 | 155 | public function testCanAddParameterSizePack(): void 156 | { 157 | $parameter = $this->toolkit->AddParameterSizePack('test comment', 'varName', 4); 158 | 159 | $this->assertTrue($parameter instanceof SizePackParam); 160 | } 161 | 162 | public function testCanSetPersistent(): void 163 | { 164 | $isPersistent = false; 165 | 166 | $this->toolkit->setIsPersistent($isPersistent); 167 | 168 | $this->assertEquals($isPersistent, $this->toolkit->getIsPersistent()); 169 | } 170 | 171 | public function testCanReturnScriptAbsolutePath(): void 172 | { 173 | $path = Toolkit::classPath(); 174 | 175 | $this->assertEquals($path, $this->toolkit->classPath()); 176 | } 177 | 178 | public function testCanGetPhpOperatingSystem(): void 179 | { 180 | $os = php_uname('s'); 181 | 182 | $this->assertEquals($os, $this->toolkit->getPhpOperatingSystem()); 183 | } 184 | 185 | public function testCanTellIfPhpIsRunningOnIbmI(): void 186 | { 187 | $isRunningOnIbmI = (php_uname('s') === 'OS400'); 188 | 189 | $this->assertEquals($isRunningOnIbmI, $this->toolkit->isPhpRunningOnIbmI()); 190 | } 191 | 192 | public function testDatabaseNameOrResourceIsNotBoolean(): void 193 | { 194 | $resource = false; 195 | $this->expectException(Exception::class); 196 | new Toolkit($resource); 197 | } 198 | 199 | public function testDatabaseNameOrResourceIsNotFloat(): void 200 | { 201 | $resource = 1.81; 202 | $this->expectException(Exception::class); 203 | new Toolkit($resource); 204 | } 205 | 206 | public function testDatabaseNameOrResourceIsNotObject(): void 207 | { 208 | $resource = new DataArea(); 209 | $this->expectException(Exception::class); 210 | new Toolkit($resource); 211 | } 212 | 213 | public function testDatabaseNameOrResourceIsNotInteger(): void 214 | { 215 | $resource = 12; 216 | $this->expectException(Exception::class); 217 | new Toolkit($resource); 218 | } 219 | 220 | public function testDatabaseNameOrResourceIsNotArray(): void 221 | { 222 | $resource = array(1, 2, 3); 223 | $this->expectException(Exception::class); 224 | new Toolkit($resource); 225 | } 226 | 227 | public function testDatabaseNameOrResourceIsNotNull(): void 228 | { 229 | $resource = null; 230 | $this->expectException(Exception::class); 231 | new Toolkit($resource); 232 | } 233 | 234 | } 235 | -------------------------------------------------------------------------------- /ToolkitApi/JobLogs.php: -------------------------------------------------------------------------------- 1 | ToolkitSrvObj = $ToolkitSrvObj; 26 | 27 | // @todo do not assume a specific plug size. 28 | $this->ToolkitSrvObj->setOptions(array('plugSize'=>'1M')); 29 | 30 | //do not use a QTEMP as temporary library. 31 | if (strcmp($tmpUSLib, "QGPL")) { 32 | $this->TmpLib = $tmpUSLib; 33 | } 34 | 35 | return $this; 36 | } else { 37 | return false; 38 | } 39 | } 40 | 41 | /** 42 | * @param $newSize 43 | */ 44 | public function setTemp_US_Size($newSize) { 45 | if ($newSize > 128000) { 46 | $this->Temp_US_Size = $newSize; 47 | } 48 | } 49 | 50 | /** 51 | * @param null $user 52 | * @param string $jobstatus 53 | * @return array|bool 54 | * @throws \Exception 55 | */ 56 | public function JobList($user = NULL, $jobstatus = "*ACTIVE") 57 | { 58 | if ($user != NULL) { 59 | $ForUser = sprintf("%-10s", $user); 60 | } else { 61 | $ForUser = "*CURRENT "; 62 | } 63 | 64 | $JobStatus = sprintf("%-10s", $jobstatus); 65 | $this->TmpUserSpace = new TmpUserSpace($this->ToolkitSrvObj, $this->TmpLib, $this->Temp_US_Size); 66 | $FullUSName = $this->TmpUserSpace->getUSFullName(); 67 | 68 | $params[]=$this->ToolkitSrvObj->AddParameterChar('input', 20, 'USER SPACE NAME', 'userspacename', $FullUSName); 69 | $params[]=$this->ToolkitSrvObj->AddParameterChar('input', 10, 'USER NAME', 'username', $ForUser); 70 | $params[]=$this->ToolkitSrvObj->AddParameterChar('input', 10, 'Job status', 'jobstatus', $JobStatus); 71 | 72 | $ret = $this->ToolkitSrvObj->PgmCall(ZSTOOLKITPGM, $this->ToolkitSrvObj->getOption('HelperLib'), $params, NULL, array ('func' => 'JOBLIST')); 73 | sleep(1); 74 | $JobList = $this->TmpUserSpace->ReadUserSpace(1, $this->TmpUserSpace->RetrieveUserSpaceSize()); 75 | 76 | $this->TmpUserSpace->DeleteUserSpace(); 77 | unset($this->TmpUserSpace); 78 | if (trim($JobList)!='') { 79 | return (str_split($JobList, $this->JOBLIST_RECORD_SIZE)); 80 | } else { 81 | return false; 82 | } 83 | } 84 | 85 | /** 86 | * @param $JobListString 87 | * @return array 88 | */ 89 | public function createJobListArray($JobListString) 90 | { 91 | $JobList = array(); 92 | if (is_array($JobListString)) { 93 | $i = 0; 94 | foreach ($JobListString as $element) 95 | { 96 | $el = str_split($element, 10); 97 | $JobList[$i]['JOBNAME'] =$el[0]; 98 | $JobList[$i]['JOBUSER'] =$el[1]; 99 | $JobList[$i]['JOBNUMBER']=$el[2]; 100 | $JobList[$i]['JOBSTATUS']=$el[3]; 101 | $JobList[$i]['JOBSUBS'] =(isset($el[4])) ? $el[4] : ''; // avoid undefined offset 102 | $JobList[$i]['ACTJOBSTATUS']=(isset($el[5])) ? $el[5] : ''; // avoid undefined offset 103 | $i++; 104 | } 105 | } 106 | 107 | return $JobList; 108 | } 109 | 110 | /** 111 | * it seems that all three parms must be entered; it's for a specific job. 112 | * 113 | * @param $JobName 114 | * @param $JobUser 115 | * @param $JobNumber 116 | * @param string $direction 117 | * @return array|bool 118 | * @throws \Exception 119 | */ 120 | public function JobLog($JobName, $JobUser, $JobNumber, $direction = 'L') 121 | { 122 | if ($JobName=='' ||$JobUser=='' || $JobNumber == '') { 123 | return false; 124 | } 125 | 126 | $this->TmpUserSpace = new TmpUserSpace($this->ToolkitSrvObj, $this->TmpLib); 127 | $FullUSName = $this->TmpUserSpace->getUSFullName(); 128 | $InputArray[]=$this->ToolkitSrvObj->AddParameterChar('input', 20, 'USER SPACE NAME', 'userspacename', $FullUSName); 129 | $InputArray[]=$this->ToolkitSrvObj->AddParameterChar('input', 10, 'JOB NAME', 'jobname', $JobName); 130 | $InputArray[]=$this->ToolkitSrvObj->AddParameterChar('input', 10, 'USER NAME', 'username', $JobUser); 131 | //L from the last log message to the first one 132 | //from the first message to the last one 133 | $dir = 'L'; 134 | 135 | if (strtoupper($direction) == "N") { 136 | $dir = $direction; 137 | } 138 | 139 | $InputArray[]=$this->ToolkitSrvObj->AddParameterChar('input', 6, 'Job Number', 'jobnumber', $JobNumber); 140 | //From the Last - "L" 141 | $InputArray[]=$this->ToolkitSrvObj->AddParameterChar('input', 1, 'Direction', 'direction', $dir); 142 | $ret_code ='0'; 143 | $InputArray[]=$this->ToolkitSrvObj->AddParameterChar('both', 1, 'retcode', 'retcode', $ret_code); 144 | 145 | $OutputArray = $this->ToolkitSrvObj->PgmCall(ZSTOOLKITPGM, $this->ToolkitSrvObj->getOption('HelperLib'), $InputArray, NULL, array('func'=>'JOBLOGINFO')); 146 | if (isset($OutputArray['io_param']['retcode'])) { 147 | //may be authorization problem 148 | if ($OutputArray['io_param']['retcode'] == '1') { 149 | //No data created in US. 150 | return false; 151 | } 152 | } 153 | 154 | sleep(1); 155 | 156 | $JobLogRows = $this->TmpUserSpace->ReadUserSpace(1, $this->TmpUserSpace->RetrieveUserSpaceSize()); 157 | $this->TmpUserSpace->DeleteUserSpace(); 158 | unset ($this->TmpUserSpace); 159 | 160 | if (trim($JobLogRows) != '') { 161 | $logArray = str_split($JobLogRows, $this->JOBLOG_RECORD_SIZE); 162 | return $logArray; 163 | } else { 164 | return false; 165 | } 166 | } 167 | 168 | /** 169 | * 170 | * @todo currentJobLog. retrieve log for it in either direction. 171 | * 172 | * @param $JobName 173 | * @param $JobUser 174 | * @param $JobNumber 175 | * @return array|bool 176 | */ 177 | public function GetJobInfo($JobName, $JobUser, $JobNumber) 178 | { 179 | /** 180 | * used format:JOBI0200 181 | */ 182 | if ($JobName=='' ||$JobUser=='' || $JobNumber == '') { 183 | return false; //nothing to show 184 | } 185 | 186 | $reciever =" "; 187 | $jobName26 = sprintf("%-10s%-10s%-6s", $JobName, $JobUser, $JobNumber); 188 | 189 | // changed 190 | $receiverSize = 1000; // 200 191 | $params[] = Toolkit::AddParameterChar('input', 26, "QualifiedJobName", 'JobName', trim($jobName26)); 192 | $params[] = Toolkit::AddParameterChar('both', $receiverSize, "reciever", 'reciever', $reciever); 193 | $ret = $this->ToolkitSrvObj->PgmCall(ZSTOOLKITPGM, $this->ToolkitSrvObj->getOption('HelperLib'), $params, NULL, array('func'=>'GETJOBINFO')); 194 | if ($ret && trim($ret['io_param']['reciever'])!='') { 195 | return ($this->parseJobInfString($ret['io_param']['reciever'])); 196 | } 197 | 198 | return false; 199 | } 200 | 201 | /** 202 | * @param $jobinfo 203 | * @return array 204 | */ 205 | private function parseJobInfString($jobinfo) 206 | { 207 | return array( 208 | 'JobName'=>substr($jobinfo, 0, 10), 209 | 'JobUser'=>substr($jobinfo, 10, 10), 210 | 'JobNumber'=>substr($jobinfo, 20, 6), 211 | 'JobStatus'=>substr($jobinfo, 26, 10), 212 | 'ActJobStat'=>substr($jobinfo, 36, 4), 213 | 'JobType'=>substr($jobinfo, 40, 1), 214 | 'JobRunPriority'=>substr($jobinfo, 41, 5), 215 | 'JobTimeSlice'=>substr($jobinfo, 46, 5), 216 | 'PoolId'=>substr($jobinfo, 51, 5), 217 | 'Functionname'=>substr($jobinfo, 56, 10), 218 | ); 219 | } 220 | } 221 | -------------------------------------------------------------------------------- /ToolkitApi/httpsupp.php: -------------------------------------------------------------------------------- 1 | XMLCGI.PGM--->XMLSERVICE 15 | * Apache conf (httpd.conf): 16 | * ScriptAlias /cgi-bin/ /QSYS.LIB/XMLSERVICE.LIB/ 17 | * 18 | * 19 | * AllowOverride None 20 | * order allow,deny 21 | * allow from all 22 | * SetHandler cgi-script 23 | * Options +ExecCGI 24 | * 25 | * 26 | * @todo define common transport class/interface extended/implemented by all transports. They have much in common. 27 | * 28 | * @package ToolkitApi 29 | */ 30 | class httpsupp 31 | { 32 | protected $last_errorcode; 33 | protected $last_errormsg; 34 | protected $_ipc; 35 | protected $_ctl; 36 | protected $_url; 37 | protected $_db; 38 | protected $_user; 39 | protected $_pw; 40 | protected $_debug; 41 | //ssl variables 42 | protected $_sslcafile; 43 | protected $_servername; 44 | 45 | /** 46 | * @param string $ipc route to XMLSERVICE job (/tmp/xmlibmdb2) 47 | * @param string $ctl XMLSERVICE control (*sbmjob) 48 | * @param string $url URL to xmlcgi.pgm (example: http://ibmi/cgi-bin/xmlcgi.pgm ) 49 | * @param string $debug *in|*out|*all (*in - dump XML input (call)) (*out - dump XML output (return)) (*all - dump XML input/output) 50 | */ 51 | public function __construct($ipc='/tmp/xmldb2', $ctl='*sbmjob', $url='http://example.com/cgi-bin/xmlcgi.pgm', $debug='*none', $sslcafile=null, $servername=null ) 52 | { 53 | $this->setIpc($ipc); 54 | $this->setCtl($ctl); 55 | $this->setUrl($url); 56 | $this->setSSLCAFile($sslcafile); 57 | $this->setServerName($servername); 58 | $this->_debug = $debug; 59 | } 60 | 61 | /** 62 | * @param $xmlIn 63 | * @param $outSize 64 | * @return string 65 | */ 66 | public function send($xmlIn, $outSize) 67 | { 68 | $clobIn = $xmlIn; 69 | $clobOut = $outSize; 70 | $postdata = http_build_query( 71 | array( 72 | 'db2' => $this->getDb(), 73 | 'uid' => $this->getUser(), 74 | 'pwd' => $this->getPw(), 75 | 'ipc' => $this->getIpc(), 76 | 'ctl' => $this->getCtl(), 77 | 'xmlin' => $clobIn, 78 | 'xmlout' => $clobOut // size expected XML output 79 | ) 80 | ); 81 | 82 | $opts = array( 83 | 'http' => 84 | array( 85 | 'method' => 'POST', 86 | 'header' => 'Content-type: application/x-www-form-urlencoded\r\n'. 87 | 'Content-Length: ' . strlen($postdata) . '\r\n', 88 | 'content' => $postdata 89 | ) 90 | ); 91 | 92 | if (substr($this->getUrl(), 0, 8) == "https://") 93 | { 94 | //If this the URL is HTTPS, then Add SSL to options to support a 95 | //secure connection. 96 | //Explanation of Options: http://phpsecurity.readthedocs.org/en/latest/Transport-Layer-Security-%28HTTPS-SSL-and-TLS%29.html 97 | $opts['ssl'] = array( 98 | 'verify_peer' => true, //Require verification of SSL certificate used. 99 | 'cafile' => $this->getSSLCAFile(), //__DIR__ . '/cacert.pem' //Location of Certificate Authority file on local filesystem which should be used with the verify_peer context option to authenticate the identity of the remote peer. PEM or CRT file. You can use this file: http://curl.haxx.se/ca/cacert.pem 100 | 'verify_depth' => 5, // Abort if the certificate chain is too deep. 101 | 'CN_match' => $this->getServerName(), //EX: secure.example.com 102 | 'disable_compression' => true, 103 | 'SNI_enabled' => true, 104 | 'ciphers' => 'ALL!EXPORT!EXPORT40!EXPORT56!aNULL!LOW!RC4'); 105 | } 106 | 107 | $context = stream_context_create($opts); 108 | // execute (call IBM i) 109 | $linkall = $this->getUrl(); 110 | 111 | if (!$linkall) { 112 | die('HTTP transport URL was not set'); 113 | } 114 | 115 | $clobOut = file_get_contents($linkall, false, $context); 116 | $clobOut = $this->driverJunkAway($clobOut); 117 | 118 | if ($this->_debug == '*all' || $this->_debug == '*in') { 119 | echo "IN->".$clobIn; 120 | } 121 | 122 | if ($this->_debug == '*all' || $this->_debug == '*out') { 123 | echo "OUT->".$clobOut; 124 | } 125 | 126 | return $clobOut; 127 | } 128 | 129 | /** 130 | * @return string 131 | */ 132 | public function getErrorCode() 133 | { 134 | return $this->last_errorcode; 135 | } 136 | 137 | /** 138 | * @return string 139 | */ 140 | public function getErrorMsg() 141 | { 142 | return $this->last_errormsg; 143 | } 144 | 145 | /** 146 | * @param $errorCode 147 | */ 148 | protected function setErrorCode($errorCode) 149 | { 150 | $this->last_errorcode = $errorCode; 151 | } 152 | 153 | /** 154 | * @param $errorMsg 155 | */ 156 | protected function setErrorMsg($errorMsg) 157 | { 158 | $this->last_errormsg = $errorMsg; 159 | } 160 | 161 | /** 162 | * @param $ipc 163 | */ 164 | public function setIpc($ipc) 165 | { 166 | $this->_ipc = $ipc; 167 | } 168 | 169 | /** 170 | * @return null 171 | */ 172 | public function getIpc() 173 | { 174 | return $this->_ipc; 175 | } 176 | 177 | /** 178 | * @param string $ctl 179 | */ 180 | public function setCtl($ctl = '') 181 | { 182 | $this->_ctl = $ctl; 183 | } 184 | 185 | /** 186 | * @return null 187 | */ 188 | public function getCtl() 189 | { 190 | return $this->_ctl; 191 | } 192 | 193 | /** 194 | * @param string $url\ 195 | */ 196 | public function setUrl($url = '') 197 | { 198 | $this->_url = $url; 199 | } 200 | 201 | /** 202 | * @return null 203 | */ 204 | public function getUrl() 205 | { 206 | return $this->_url; 207 | } 208 | 209 | /** 210 | * @param null $sslcafile 211 | * @throws \Exception 212 | */ 213 | public function setSSLCAFile($sslcafile = null) 214 | { 215 | if(substr($this->getUrl(), 0, 8) == "https://" && $sslcafile==null) 216 | { 217 | throw new \Exception('No SSL CA File specified. Must pass in CA File to use https url'); 218 | } 219 | $this->_sslcafile = $sslcafile; 220 | } 221 | 222 | /** 223 | * @return null 224 | */ 225 | public function getSSLCAFile() 226 | { 227 | return $this->_sslcafile; 228 | } 229 | 230 | /** 231 | * @param string $servername\ 232 | */ 233 | public function setServerName($servername = null) 234 | { 235 | if($servername==null && isset($_SERVER['SERVER_NAME'])) 236 | { 237 | $servername=$_SERVER['SERVER_NAME']; 238 | } 239 | $this->_servername = $servername; 240 | } 241 | 242 | public function getServerName() 243 | { 244 | return $this->_servername; 245 | } 246 | 247 | /** 248 | * 249 | * @todo shared transport method 250 | * 251 | * @param $xml 252 | * @return string 253 | */ 254 | public function driverJunkAway($xml) 255 | { 256 | // trim blanks 257 | $clobOut = trim($xml); 258 | if (!$clobOut) return $clobOut; 259 | 260 | // result set has extra data (junk) 261 | $fixme = ''; 262 | $pos = strpos($clobOut,$fixme); 263 | 264 | if ($pos > -1) { 265 | $clobOut = substr($clobOut,0,$pos+strlen($fixme)); 266 | } else { // maybe error/performance report 267 | $fixme = ''; 268 | $pos = strpos($clobOut,$fixme); 269 | 270 | if ($pos > -1) { 271 | $clobOut = substr($clobOut,0,$pos+strlen($fixme)); 272 | } 273 | } 274 | 275 | return $clobOut; 276 | } 277 | 278 | /** 279 | * @param string $db 280 | * @param $user 281 | * @param $pw 282 | * @return $this 283 | */ 284 | public function http_connect($db, $user, $pw) 285 | { 286 | // accommodate ('', '', '') style of connection 287 | if(!$db) { 288 | $db = '*LOCAL'; 289 | } 290 | 291 | $this->_db = $db; 292 | $this->_user = $user; 293 | $this->_pw = $pw; 294 | 295 | return $this; 296 | } 297 | 298 | /** 299 | * @param string $database 300 | * @param $user 301 | * @param $password 302 | * @return httpsupp 303 | */ 304 | public function connect($database, $user, $password) 305 | { 306 | // accommodate ('', '', '') style of connection 307 | if(!$database) { 308 | $database = '*LOCAL'; 309 | } 310 | 311 | return $this->http_connect($database, $user, $password); 312 | 313 | } 314 | 315 | /** 316 | * @return null 317 | */ 318 | protected function getUser() 319 | { 320 | return $this->_user; 321 | } 322 | 323 | /** 324 | * @return null 325 | */ 326 | protected function getPw() 327 | { 328 | return $this->_pw; 329 | } 330 | 331 | /** 332 | * @return null 333 | */ 334 | protected function getDb() 335 | { 336 | return $this->_db; 337 | } 338 | } 339 | -------------------------------------------------------------------------------- /ToolkitApi/DataArea.php: -------------------------------------------------------------------------------- 1 | ToolkitSrvObj = $ToolkitSrvObj; 23 | return $this; 24 | } else { 25 | return false; 26 | } 27 | } 28 | 29 | /** 30 | * for *char data area. According to create other data area types 31 | * use CL command 32 | * 33 | * @param string $DataAreaName 34 | * @param string $DataAreaLib *CURLIB is correct, the default with CRTDTAARA. 35 | * @param int $size 36 | * @return bool 37 | * @throws \Exception 38 | */ 39 | public function createDataArea($DataAreaName = '', $DataAreaLib = "*CURLIB", $size = 2000) 40 | { 41 | if ($DataAreaName !='' && $this->DataAreaName == NULL) { /*was not set before*/ 42 | $this->setDataAreaName($DataAreaName , $DataAreaLib); 43 | } 44 | 45 | if ($size > 2000 || $size <= 0) { 46 | $dataAreaLen = 2000; 47 | } else { 48 | $dataAreaLen = $size; 49 | } 50 | 51 | $cmd = sprintf("QSYS/CRTDTAARA DTAARA(%s/%s) TYPE(*CHAR) LEN($dataAreaLen)", 52 | ($DataAreaLib != '' ? $DataAreaLib : $this->DataAreaLib), 53 | ($DataAreaName !='' ? $DataAreaName : $this->DataAreaName)); 54 | 55 | // @todo get CPF code 56 | if (!$this->ToolkitSrvObj->CLCommand($cmd)) { 57 | $this->ErrMessage = "Create Data Area failed." . $this->ToolkitSrvObj->getLastError(); 58 | throw new \Exception($this->ErrMessage); 59 | } 60 | 61 | return true; 62 | } 63 | 64 | /** 65 | * @return string 66 | */ 67 | private function getAPIDataAreaName() 68 | { 69 | return (sprintf("%-10s%-10s", $this->DataAreaName, $this->DataAreaLib)); 70 | } 71 | 72 | /** 73 | * @return null|string 74 | */ 75 | protected function getQualifiedDataAreaName() { 76 | // return dtaara name in lib/dtaara format. 77 | if ($this->DataAreaLib) { 78 | return "{$this->DataAreaLib}/{$this->DataAreaName}"; 79 | } else { 80 | // no library (e.g. *LDA dtaara). Return only dtaara name. 81 | return $this->DataAreaName; 82 | } 83 | } 84 | 85 | /** 86 | * *LIBL to read/write data area. *CURLIB to create. 87 | * 88 | * @param $dataAreaName 89 | * @param string $dataAreaLib 90 | * @throws \Exception 91 | */ 92 | public function setDataAreaName($dataAreaName, $dataAreaLib = "*LIBL") 93 | { 94 | /** 95 | * special values: 96 | * LDA Local data area 97 | * GDA Group data area 98 | * PDA Program initialization parameter data area 99 | */ 100 | $dataAreaName = trim(strtoupper($dataAreaName)); 101 | 102 | if ($dataAreaName == '') { 103 | throw new \Exception("Data Area name parameter should be defined "); 104 | } 105 | 106 | // no library allowed for these special values. 107 | if (in_array($dataAreaName, array('*LDA', '*GDA', '*PDA'))) { 108 | $dataAreaLib = ''; 109 | } 110 | 111 | $this->DataAreaName = $dataAreaName; 112 | $this->DataAreaLib = $dataAreaLib; 113 | } 114 | 115 | /** 116 | * @param int $fromPosition 117 | * @param string $dataLen 118 | * @return bool 119 | */ 120 | public function readDataArea($fromPosition = 1 , $dataLen = '*ALL') 121 | { 122 | if (!$this->ToolkitSrvObj instanceof Toolkit) 123 | return false; 124 | 125 | $Err = ' '; 126 | $value = ''; 127 | if ($fromPosition == 0) { 128 | $fromPosition = 1; 129 | } 130 | 131 | $maxValueSize = 2000; // largest allowed data area size 132 | 133 | $adjustedStartRequested = $fromPosition; 134 | $adjustedLengthRequested = $dataLen; 135 | 136 | // if data len is *ALL, position and length receive special values.. 137 | if (strtoupper($dataLen) == '*ALL') { // either numeric or *ALL 138 | $adjustedStartRequested = -1; // *ALL; 139 | $adjustedLengthRequested = $maxValueSize; 140 | } 141 | 142 | $toolkit = $this->ToolkitSrvObj; 143 | 144 | /** 145 | * Retrieve Data Area (QWCRDTAA) API 146 | * http://publib.boulder.ibm.com/infocenter/iseries/v5r3/index.jsp?topic=%2Fapis%2Fqwcrdtaa.htm 147 | * 148 | * Required Parameter Group: 149 | * 1 Receiver variable Output Char(*) 150 | * 2 Length of receiver variable Input Binary(4) Max length is 2000 151 | * 3 Qualified data area name Input Char(20) 152 | * 4 Starting position Input Binary(4) 153 | * 5 Length of data Input Binary(4) 154 | * 6 Error code I/O Char(*) 155 | * 156 | * 157 | * format of receiver variable 158 | * 0 0 BINARY(4) Bytes available (The length of all data available to return. All available data is returned if enough space is provided.) 159 | * 4 4 BINARY(4) Bytes returned (The length of all data actually returned) 160 | * 8 8 CHAR(10) Type of value returned (*CHAR, *DEC, *LGL) 161 | * 18 12 CHAR(10) Library name (blank if *LDA et al.) 162 | * 28 1C BINARY(4) Length of value returned 163 | * 32 20 BINARY(4) Number of decimal positions 164 | * 36 24 CHAR(*) Value (contents of data area) 165 | */ 166 | 167 | // @todo allow data structure in data area, if packed/binary allowed. 168 | 169 | $receiverVar = array(); 170 | $receiverVar[] = $toolkit->AddParameterInt32('out', 'Bytes available: length of all data available to return', 'bytesAvail', $maxValueSize); 171 | $receiverVar[] = $toolkit->AddParameterInt32('out', 'Length of all data returned, limited by size of receiver', 'bytesReturned', 0); 172 | $receiverVar[] = $toolkit->AddParameterChar('out', '10', 'Type of value returned (*CHAR, *DEC, *LGL)', 'Type', ''); 173 | $receiverVar[] = $toolkit->AddParameterChar('out', '10', 'Library where data area was found', 'Library', ''); 174 | $receiverVar[] = $toolkit->AddParameterInt32('out', 'Length of value returned', 'lengthReturned', 0); 175 | $receiverVar[] = $toolkit->AddParameterInt32('out', 'Number of decimal positions', 'decimalPositions', 0); 176 | $receiverVar[] = $toolkit->AddParameterChar('out', $maxValueSize, 'Value returned', 'value', ''); // set length to $maxValueSize to be safe 177 | 178 | $receiverLength = $maxValueSize + 36; // 4 4-byte integers + 2 10-byte character strings = 36 179 | 180 | // "NAME LIB " no slash. Left-aligned. 181 | $twentyCharQualifiedName = $this->getAPIDataAreaName(); 182 | 183 | $toolkitParams = array(); 184 | $toolkitParams [] = $toolkit->AddDataStruct($receiverVar, 'receiver'); 185 | $toolkitParams [] = $toolkit->AddParameterInt32('in', 'Length of receiver variable', 'receiverLen', $receiverLength); 186 | $toolkitParams [] = $toolkit->AddParameterChar('in', 20, 'Data area name', 'DtaaraName', $twentyCharQualifiedName); 187 | // Starting position: The first byte of the data area to be retrieved. A value of 1 will identify the first character in the data area. The maximum value allowed for the starting position is 2000. A value of -1 will return all the characters in the data area. 188 | $toolkitParams [] = $toolkit->AddParameterInt32('in', 'Starting position requested', 'fromPosition', $adjustedStartRequested); 189 | $toolkitParams [] = $toolkit->AddParameterInt32('in', 'Data length requested', 'dataLength', $adjustedLengthRequested); 190 | $toolkitParams [] = $toolkit->AddErrorDataStructZeroBytes(); // so errors bubble up to joblog 191 | 192 | // we're using a data structure here so integrity must be on 193 | // @todo pass as option on the program call 194 | $dsIntegrity = $toolkit->getOption('dataStructureIntegrity'); // save original value 195 | $toolkit->setOptions(array('dataStructureIntegrity'=>true)); 196 | 197 | $retPgmArr = $toolkit->PgmCall('QWCRDTAA', '', $toolkitParams); 198 | 199 | $toolkit->setOptions(array('dataStructureIntegrity'=>$dsIntegrity)); // restore original value 200 | 201 | // check for any errors 202 | if ($toolkit->getErrorCode()) { 203 | // an error 204 | return false; 205 | } else { 206 | // extricate the data from the receiver variable ds wrapper 207 | $value = $retPgmArr['io_param']['receiver']['value']; 208 | } 209 | 210 | return ($value) ? $value : false; 211 | } 212 | 213 | /** 214 | * @param $msg 215 | */ 216 | private function setError($msg){ 217 | $this->ErrMessage = $msg; 218 | } 219 | 220 | /** 221 | * @return mixed 222 | */ 223 | public function getError() { 224 | return $this->ErrMessage; 225 | } 226 | 227 | /** 228 | * @param $value 229 | * @param int $fromPosition 230 | * @param int $dataLen 231 | * @throws \Exception 232 | */ 233 | public function writeDataArea($value, $fromPosition = 0, $dataLen = 0) 234 | { 235 | $substring = ''; // init 236 | if ($fromPosition > 0) { 237 | $substring = sprintf("(%d %d)", $fromPosition, $dataLen); 238 | } 239 | 240 | // @todo use API instead. Handle numeric and character data., *CHAR and *DEC as well. 241 | // and/or quote the string. Needs to be case-sensitive and handle numeric input. 242 | $cmd = sprintf("CHGDTAARA DTAARA(%s $substring) VALUE($value)", 243 | $this->getQualifiedDataAreaName()); 244 | 245 | if (!$this->ToolkitSrvObj->CLCommand($cmd)) { 246 | $this->ErrMessage = "Write into Data Area failed." . $this->ToolkitSrvObj->getLastError(); 247 | throw new \Exception($this->ErrMessage); 248 | } 249 | } 250 | 251 | /** 252 | * requires explicit library 253 | * 254 | * @param string $DataAreaName 255 | * @param string $DataAreaLib 256 | * @throws \Exception 257 | */ 258 | public function deleteDataArea($DataAreaName = '', $DataAreaLib = '') 259 | { 260 | $cmd = sprintf("QSYS/DLTDTAARA DTAARA(%s/%s)", 261 | ($DataAreaLib != '' ? $DataAreaLib : $this->DataAreaLib), 262 | ($DataAreaName != NULL ? $DataAreaName : $this->DataAreaName)); 263 | 264 | if (!$this->ToolkitSrvObj->CLCommand($cmd)) { 265 | $this->ErrMessage = "Delete Data Area failed." . $this->ToolkitSrvObj->getLastError(); 266 | throw new \Exception($this->ErrMessage); 267 | } 268 | } 269 | } 270 | -------------------------------------------------------------------------------- /ToolkitApi/DataQueue.php: -------------------------------------------------------------------------------- 1 | Toolkit = $ToolkitSrvObj ; 24 | return $this; 25 | } 26 | 27 | return false; 28 | } 29 | 30 | /** 31 | * @return mixed 32 | */ 33 | public function getError() 34 | { 35 | return $this->ErrMessage; 36 | } 37 | 38 | /** 39 | * @param $DataQName 40 | * @param $DataQLib 41 | * @param int $MaxLength 42 | * @param string $Sequence 43 | * @param int $KeyLength 44 | * @param string $Authority 45 | * @param int $QSizeMaxNumEntries 46 | * @param int $QSizeInitNumEntries 47 | * @return bool 48 | * @throws \Exception 49 | */ 50 | public function CreateDataQ($DataQName, $DataQLib, 51 | $MaxLength=128, 52 | $Sequence ='*FIFO', $KeyLength=0, 53 | $Authority = '*LIBCRTAUT', 54 | $QSizeMaxNumEntries=32999, $QSizeInitNumEntries = 16) 55 | { 56 | $this->DataQueueName = $DataQName; 57 | $this->DataQueueLib = $DataQLib; 58 | 59 | if (strcmp(strtoupper($Sequence), '*KEYED') == 0|| 60 | strcmp(strtoupper($Sequence), '*FIFO')== 0 || 61 | strcmp(strtoupper($Sequence), '*LIFO')== 0) { 62 | $DataQType = $Sequence; 63 | } else { 64 | return $this->SetError("Invalid Data Queue type parameter"); 65 | } 66 | 67 | $KeyedSetting = ''; 68 | 69 | if (strcmp(strtoupper($Sequence), '*KEYED') == 0) { 70 | $DQKeylen = min($KeyLength, 256 ); 71 | $KeyedSetting = "KEYLEN($DQKeylen)"; 72 | } 73 | 74 | // @todo validation: if $KeyLength supplied, sequence must be *KEYED. 75 | 76 | if (is_integer($QSizeMaxNumEntries)) { 77 | $MaxQSize = $QSizeMaxNumEntries; 78 | } else { 79 | if (strcmp($QSizeMaxNumEntries, '*MAX16MB') == 0 || strcmp($QSizeMaxNumEntries, '*MAX2GB')== 0) { 80 | $MaxQSize = (string) $QSizeMaxNumEntries; 81 | } 82 | } 83 | 84 | if ($QSizeInitNumEntries > 0) { 85 | $InitEntryies = $QSizeInitNumEntries; 86 | } 87 | 88 | $AdditionalSetting = sprintf("$KeyedSetting SENDERID(*YES) SIZE(%s %d)", $MaxQSize, $InitEntryies); 89 | 90 | ($MaxLength > 64512) ? $MaxLen = 64512: $MaxLen = $MaxLength; 91 | 92 | $cmd = sprintf("QSYS/CRTDTAQ DTAQ(%s/%s) MAXLEN(%s) SEQ(%s) %s AUT(%s)", 93 | $this->DataQueueLib,$this->DataQueueName, 94 | $MaxLen, $DataQType, $AdditionalSetting, $Authority); 95 | 96 | if (!$this->Toolkit->CLCommand($cmd)) { 97 | $this->ErrMessage = "Create Data Queue failed.". $this->Toolkit->getLastError(); 98 | throw new \Exception($this->ErrMessage); 99 | } 100 | return true; 101 | } 102 | 103 | /** 104 | * @param string $DataQName 105 | * @param string $DataQLib 106 | * @return bool 107 | * @throws \Exception 108 | */ 109 | public function DeleteDQ($DataQName ='', $DataQLib = '') 110 | { 111 | $cmd = sprintf("QSYS/DLTDTAQ DTAQ(%s/%s)", 112 | ($DataQLib != '' ? $DataQLib : $this->DataQueueLib), 113 | ($DataQName != NULL ? $DataQName : $this->DataQueueName)); 114 | 115 | if (!$this->Toolkit->CLCommand($cmd)) { 116 | $this->ErrMessage = "Delete Data Queue failed.". $this->Toolkit->getLastError(); 117 | throw new \Exception($this->ErrMessage); 118 | } 119 | 120 | return true; 121 | } 122 | 123 | /** 124 | * Correct spelling with this alias 125 | * 126 | * @param $WaitTime 127 | * @param string $KeyOrder 128 | * @param int $KeyLength 129 | * @param string $KeyData 130 | * @param string $WithRemoveMsg 131 | * @return bool 132 | */ 133 | public function receiveDataQueue($WaitTime, $KeyOrder = '', $KeyLength = 0, $KeyData = '', $WithRemoveMsg = 'N') 134 | { 135 | // call misspelled one 136 | return $this->receieveDataQueue($WaitTime, $KeyOrder, $KeyLength, $KeyData, $WithRemoveMsg); 137 | } 138 | 139 | /** 140 | * @param $WaitTime 141 | * @param string $KeyOrder 142 | * @param int $KeyLength 143 | * @param string $KeyData 144 | * @param string $WithRemoveMsg 145 | * @return bool 146 | */ 147 | public function receieveDataQueue($WaitTime, $KeyOrder = '', $KeyLength = 0, $KeyData = '', $WithRemoveMsg = 'N') 148 | { 149 | // uses QRCVDTAQ API 150 | // http://publib.boulder.ibm.com/infocenter/iseries/v5r3/index.jsp?topic=%2Fapis%2Fqrcvdtaq.htm 151 | 152 | $params [] = $this->Toolkit->AddParameterChar('in', 10, 'dqname', 'dqname', $this->DataQueueName); 153 | $params [] = $this->Toolkit->AddParameterChar('in', 10, 'dqlib', 'dqlib', $this->DataQueueLib); 154 | 155 | // @todo do not hard-code data size. Use system of labels as allowed by XMLSERVICE (as done in CW's i5_dtaq_receive). 156 | $DataLen = 300; 157 | $Data = ' '; 158 | 159 | $params [] = $this->Toolkit->AddParameterPackDec('out', 5, 0, 'datalen', 'datalen', $DataLen); // @todo this is output only so no need to specify a value 160 | $params [] = $this->Toolkit->AddParameterChar('out', (int) $DataLen, 'datavalue', 'datavalue', $Data); // @todo this is output only so no need to specify a value. 161 | 162 | // Wait time: < 0 waits forever. 0 process immed. > 0 is number of seconds to wait. 163 | $params [] = $this->Toolkit->AddParameterPackDec('in', 5, 0, 'waittime', 'waittime', $WaitTime); 164 | 165 | if (!$KeyLength) { 166 | // 0, make order, length and data also zero or blank, so thatthey'll be ignored by API. Must send them, though. 167 | 168 | // if an unkeyed queue, API still expects to receive key info, 169 | // but it must be blank and zero. 170 | $KeyOrder = ''; // e.g. EQ, other operators, or blank 171 | $KeyLength = 0; 172 | $KeyData = ''; 173 | } 174 | 175 | $params [] = $this->Toolkit->AddParameterChar('in', 2, 'keydataorder', 'keydataorder', $KeyOrder); 176 | $params [] = $this->Toolkit->AddParameterPackDec('in', 3, 0, 'keydatalen', 'keydatalen', $KeyLength); 177 | $params [] = $this->Toolkit->AddParameterChar('both', (int) $KeyLength, 'keydata', 'keydata', $KeyData); 178 | 179 | $params [] = $this->Toolkit->AddParameterPackDec('in', 3, 0, 'senderinflen', 'senderinflen', 44); 180 | // Sender info may contain packed data, so don't receive it till we can put it in a data structure. 181 | // @todo use a data structure to receive sender info as defined in QRCVDTAQ spec. 182 | $params [] = $this->Toolkit->AddParameterHole(44, 'senderinf'); 183 | 184 | // whether to remove message from data queue 185 | if ($WithRemoveMsg == 'N') { 186 | $Remove= '*NO '; 187 | } else { 188 | $Remove= '*YES '; 189 | } 190 | 191 | $params[] = $this->Toolkit->AddParameterChar('in', 10, 'remove', 'remove', $Remove); 192 | // @todo note from API manual: If this parameter is not specified, the entire message will be copied into the receiver variable. 193 | $params[] = $this->Toolkit->AddParameterPackDec('in', 5, 0, 'size of data receiver', 'receiverSize', $DataLen); 194 | 195 | $params[] = $this->Toolkit->AddErrorDataStructZeroBytes(); // so errors bubble up to joblog 196 | 197 | $retPgmArr = $this->Toolkit->PgmCall('QRCVDTAQ', 'QSYS', $params); 198 | if (isset($retPgmArr['io_param'])) { 199 | $DQData = $retPgmArr['io_param']; 200 | 201 | if ($DQData['datalen']> 0) { 202 | return $DQData; 203 | } 204 | } 205 | 206 | return false; 207 | } 208 | 209 | /** 210 | * @param $DataQName 211 | * @param $DataQLib 212 | */ 213 | public function SetDataQName($DataQName, $DataQLib) 214 | { 215 | $this->DataQueueName = $DataQName; 216 | $this->DataQueueLib = $DataQLib; 217 | } 218 | 219 | /** 220 | * @param $DataLen 221 | * @param $Data 222 | * @param int $KeyLength 223 | * @param string $KeyData 224 | * @return array|bool 225 | */ 226 | public function SendDataQueue($DataLen, $Data, $KeyLength=0, $KeyData='') 227 | { 228 | // QSNDDTAQ API: 229 | // http://publib.boulder.ibm.com/infocenter/iseries/v5r4/index.jsp?topic=%2Fapis%2Fqsnddtaq.htm 230 | 231 | $params[] = $this->Toolkit->AddParameterChar('in', 10, 'dqname', 'dqname', $this->DataQueueName); 232 | $params[] = $this->Toolkit->AddParameterChar('in', 10, 'dqlib', 'dqlib',$this->DataQueueLib); 233 | 234 | $params[] = $this->Toolkit->AddParameterPackDec('in', 5, 0, 'datalen', 'datalen', $DataLen, null); 235 | $params[] = $this->Toolkit->AddParameterChar('in', $DataLen, 'datavalue','datavalue', $Data); 236 | if ($KeyLength > 0 ) { 237 | $params[] = $this->Toolkit->AddParameterPackDec('in', 3, 0, 'keydatalen', 'keydatalen', $KeyLength, null); 238 | $params[] = $this->Toolkit->AddParameterChar('in', $KeyLength, 'keydata', 'keydata', $KeyData); 239 | } 240 | 241 | $ret = $this->Toolkit->PgmCall('QSNDDTAQ', 'QSYS', $params); 242 | 243 | return $ret; 244 | } 245 | 246 | /** 247 | * @param string $KeyOrder 248 | * @param int $KeyLength 249 | * @param string $KeyData 250 | * @return bool 251 | */ 252 | public function ClearDQ($KeyOrder= '', $KeyLength=0, $KeyData='') 253 | { 254 | //QCLRDTAQ 255 | $params[]=$this->Toolkit->AddParameterChar('in', 10, 'dqname', 'dqname', $this->DataQueueName); 256 | $params[]=$this->Toolkit->AddParameterChar('in', 10, 'dqlib', 'dqlib', $this->DataQueueLib); 257 | if ($KeyLength > 0) { 258 | $params[] = $this->Toolkit->AddParameterChar('in', 2, 'keydataorder', 'keydataorder', $KeyOrder); 259 | $params[] = $this->Toolkit->AddParameterPackDec('in', 3, 0, 'keydatalen', 'keydatalen', $KeyLength); 260 | $params[] = $this->Toolkit->AddParameterChar('in', ((int)$KeyLength), 'keydata', 'keydata', $KeyData); 261 | //$params[] = array('ds'=>$this->Toolkit->GenerateErrorParameter()); 262 | $ds =$this->Toolkit->GenerateErrorParameter(); 263 | $params[] = Toolkit::AddDataStruct($ds); 264 | } 265 | 266 | $retArr = $this->Toolkit->PgmCall('QCLRDTAQ', 'QSYS', $params); 267 | 268 | if (isset($retArr['exceptId']) && strcmp ($retArr['exceptId'], '0000000')) { 269 | $this->CPFErr = $retArr['exceptId']; 270 | $this->ErrMessage ="Clear Data Queue failed. Error: $this->CPFErr"; 271 | return false; 272 | } 273 | 274 | return true; 275 | } 276 | } 277 | -------------------------------------------------------------------------------- /ToolkitApi/CW/DataDescriptionPcml.php: -------------------------------------------------------------------------------- 1 | I5_TYPE_CHAR, 14 | 'packed' => I5_TYPE_PACKED, 15 | // 4 byte float 16 | 'float' => I5_TYPE_FLOAT, 17 | // data structure 18 | 'struct' => I5_TYPE_STRUCT, 19 | // omit INT from type map because we'll need program logic to determine if short or regular int. 20 | 'zoned' => I5_TYPE_ZONED, 21 | // TODO not sure if byte really maps to binary. No one knows what BYTE really does 22 | 'byte' => I5_TYPE_BYTE, 23 | ); 24 | 25 | // PCML usage mapping 26 | protected $_pcmlInoutMap = array('input' => I5_IN, 27 | 'output' => I5_OUT, 28 | 'inputoutput' => I5_INOUT, 29 | // inherit means inherit from parent element, and if no parent element, do INOUT. 30 | // TODO implement "inherit" more precisely, checking parent element's usage. 31 | 'inherit' => I5_INOUT, 32 | ); 33 | 34 | // maintain an array of pcml structures 35 | protected $_pcmlStructs = array(); 36 | 37 | /** 38 | * Constructor takes a PCML string and converts to an array-based old toolkit data description string. 39 | * 40 | * @param string $pcml The string of PCML 41 | * @param ToolkitInterface $connection connection object for toolkit 42 | * @throws \Exception 43 | */ 44 | public function __construct($pcml, ToolkitInterface $connection) 45 | { 46 | $this->setConnection($connection); 47 | 48 | // Convert PCML from ANSI format (which old toolkit required) to UTF-8 (which SimpleXML requires). 49 | 50 | $encoding = $connection->getConfigValue('system', 'encoding', 'ISO-8859-1'); // XML encoding 51 | 52 | /* 53 | * Look for optionally set /is'; 60 | if (preg_match($regex, $pcml, $matches) && $matches[1] != 'UTF-8') { 61 | //remove xml-tag 62 | $pcml = substr($pcml, strlen($matches[0])); 63 | $pcml = mb_convert_encoding($pcml, 'UTF-8', $matches[1]); 64 | } elseif ($encoding != 'UTF-8') { 65 | $pcml = mb_convert_encoding($pcml, 'UTF-8', $encoding); 66 | } 67 | 68 | //program name is stored as: /pcml/program name="/qsys.lib/eacdemo.lib/teststruc.pgm" 69 | $xmlObj = new \SimpleXMLElement($pcml); 70 | 71 | // get root node and make sure it's named 'pcml' 72 | if(!isset($xmlObj[0]) || ($xmlObj[0]->getName() != 'pcml')) { 73 | throw new \Exception("PCML file must contain pcml tag"); 74 | } 75 | 76 | $pcmlObj = $xmlObj[0]; 77 | 78 | // get program name, path, etc. 79 | if(!isset($pcmlObj->program) || (!$pcmlObj->program)) { 80 | throw new \Exception("PCML file must contain program tag"); 81 | } 82 | $programNode = $pcmlObj->program; 83 | 84 | $pgmAttrs = $programNode->attributes(); 85 | 86 | /** 87 | * sample: 88 | * 95 | * 96 | */ 97 | 98 | // let's focus on name, path, and entrypoint, the only attributes likely to be used here. 99 | $path = (isset($pgmAttrs['path'])) ? $pgmAttrs['path'] : ''; 100 | $entrypoint = (isset($pgmAttrs['entrypoint'])) ? $pgmAttrs['entrypoint'] : ''; 101 | 102 | // Note: if entrypoint is supplied, it's the function in a service program. "name" will be the same as entrypoint. 103 | // if entrypoint is not supplied, name is the actual program name. 104 | // Therefore, "name" seems somewhat worthless. 105 | 106 | // break up path, separated now by slashes. can be varied lib and pgm. 107 | // remove the /qsys.lib that may be in front but only if it's simply qualifying another library. qsys may be the actual program library, too. 108 | 109 | $objArray = $this->splitPcmlProgramPath($path); 110 | if ($objArray['lib']) { 111 | $pgmName = "{$objArray['lib']}/{$objArray['obj']}"; 112 | } else { 113 | $pgmName = $objArray['obj']; 114 | } 115 | 116 | // now add the entrypoint, if any, as a procedure/function. 117 | if ($entrypoint) { 118 | // append the entry point enclosed in parentheses. 119 | $pgmName .= "($entrypoint)"; 120 | } 121 | 122 | // Now create data description array. 123 | $dataDescriptionArray = $this->pcmlToArray($xmlObj); 124 | 125 | //Change the encoding back to the one wanted by the user, since SimpleXML encodes its output always in UTF-8 126 | $pgmName = mb_convert_encoding($pgmName, $encoding, 'UTF-8'); 127 | mb_convert_variables($encoding, 'UTF-8', $dataDescriptionArray); 128 | 129 | // call parent's constructor with: 130 | //$descObj = new DataDescriptionPcml($description, $connection); 131 | parent::__construct($pgmName, $dataDescriptionArray, $connection); 132 | } 133 | 134 | /** 135 | * given a single ->data or ->struct element, return an array containing its contents as old toolkit-style data description. 136 | * 137 | * @param \SimpleXmlElement $dataElement 138 | * @return array 139 | */ 140 | public function singlePcmlToArray(\SimpleXmlElement $dataElement) 141 | { 142 | $tagName = $dataElement->getName(); 143 | 144 | // get attributes of this element. 145 | $attrs = $dataElement->attributes(); 146 | 147 | // both struct and data have name, count (optional), usage 148 | $name = (isset($attrs['name'])) ? (string) $attrs['name'] : ''; 149 | $count = (isset($attrs['count'])) ? (string) $attrs['count'] : ''; 150 | $usage = (isset($attrs['usage'])) ? (string) $attrs['usage'] : ''; 151 | $structName = (isset($attrs['struct'])) ? (string) $attrs['struct'] : ''; 152 | 153 | // fill this if we have a struct 154 | $subElements = array(); 155 | 156 | // should all be data 157 | if ($tagName == 'data') { 158 | 159 | $type = (isset($attrs['type'])) ? (string) $attrs['type'] : ''; 160 | 161 | // if a struct then we need to recurse. 162 | if ($type != 'struct') { 163 | 164 | // regular type (char, int...), not a struct, so the data element's name is just 'name'. 165 | $nameName = 'Name'; 166 | } else { 167 | // it IS a struct. 168 | 169 | // old toolkit uses DSName for a data structure's name. 170 | $nameName = 'DSName'; 171 | 172 | $theStruct = null; // init 173 | 174 | // look for matching struct 175 | if ($this->_pcmlStructs) { 176 | // TODO verify type with is_array and count 177 | foreach ($this->_pcmlStructs as $possibleStruct) { 178 | $possStructAttrs = $possibleStruct->attributes(); 179 | if ($possStructAttrs['name'] == $structName) { 180 | $theStruct = $possibleStruct; 181 | $structAttrs = $possStructAttrs; 182 | break; 183 | } 184 | } 185 | } 186 | 187 | // if struct was not found, generate error for log 188 | if (!$theStruct) { 189 | // $this->getConnection->logThis("PCML structure '$structName' not found."); 190 | return null; 191 | } 192 | 193 | // if we got here, we found our struct. 194 | 195 | // count can also be defined at the structure level. If so, it will override count from data level) 196 | if (isset($structAttrs['count'])) { 197 | $count = (string) $structAttrs['count']; 198 | } 199 | 200 | // "usage" (in/out/inherit) can be defined here, at the structure level. 201 | $structUsage = (isset($structAttrs['usage'])) ? (string) $structAttrs['usage'] : ''; 202 | 203 | // if we're not inheriting from our parent data element, but there is a struct usage, use the struct's usage (input, output, or inputoutput). 204 | if (!empty($structUsage) && ($structUsage != 'inherit')) { 205 | $usage = $structUsage; 206 | } 207 | 208 | $structSubDataElementsXmlObj = $theStruct->xpath('data'); 209 | if ($structSubDataElementsXmlObj) { 210 | foreach ($structSubDataElementsXmlObj as $subDataElementXmlObj) { 211 | 212 | if ($subDataElementXmlObj->attributes()->usage == 'inherit') { 213 | // subdata is inheriting type from us. Give it to them. 214 | $subDataElementXmlObj->attributes()->usage = $usage; 215 | } 216 | 217 | // here's where the recursion comes in. Convert data and add to array for our struct. 218 | $subElements[] = $this->singlePcmlToArray($subDataElementXmlObj); 219 | } 220 | } 221 | } 222 | 223 | $length = (isset($attrs['length'])) ? (string) $attrs['length'] : ''; 224 | $precision = (isset($attrs['precision'])) ? (string) $attrs['precision'] : ''; 225 | 226 | //$struct = (isset($attrs['struct'])) ? (string) $attrs['struct'] : ''; // if this is pointing to a struct name 227 | 228 | // find CW data type equivalent of PCML data type 229 | if (isset($this->_pcmlTypeMap[$type])) { 230 | // a simple type mapping 231 | $newType = (string) $this->_pcmlTypeMap[$type]; 232 | } elseif ($type == 'int') { 233 | // one of the integer types. Need to use length to determine which one. 234 | if ($length == '2') { 235 | $newType = I5_TYPE_SHORT; 236 | } elseif ($length == '4') { 237 | $newType = I5_TYPE_INT; 238 | } else { 239 | $newType = ''; // no match 240 | } 241 | } else { 242 | $newtype = ''; 243 | 244 | } 245 | 246 | $newInout = (isset($this->_pcmlInoutMap[$usage])) ? (string) $this->_pcmlInoutMap[$usage] : ''; 247 | 248 | // create new length using precision if necessary 249 | if ($precision) { 250 | $newLength = "$length.$precision"; 251 | } else { 252 | $newLength = $length; 253 | } 254 | } 255 | 256 | // count 257 | $newCount = 0; // initialize 258 | $newCountRef = ''; 259 | if (is_numeric($count) && ($count > 0)) { 260 | $newCount = $count; 261 | } elseif (is_string($count) && !empty($count)) { 262 | // count is character, so it's really a countref 263 | $newCountRef = $count; 264 | } 265 | 266 | $element = array(); 267 | 268 | $element[$nameName] = $name; 269 | 270 | // if not a struct, provide data type. 271 | if ($type != 'struct') { 272 | $element['Type'] = $newType; 273 | } 274 | 275 | if ($newCount) { 276 | $element['Count'] = $newCount; 277 | } 278 | if ($newCountRef) { 279 | $element['CountRef'] = $newCountRef; 280 | } 281 | if ($newLength) { 282 | $element['Length'] = $newLength; 283 | } 284 | if ($newInout) { 285 | $element['IO'] = $newInout; 286 | } 287 | 288 | if (count($subElements)) { 289 | $element['DSParm'] = $subElements; 290 | } 291 | 292 | return $element; 293 | } 294 | 295 | /** 296 | * given an XML object containing a PCML program definition, return an old toolkit style of data description array. 297 | * 298 | * @param \SimpleXMLElement $xmlObj 299 | * @return array 300 | */ 301 | public function pcmlToArray(\SimpleXMLElement $xmlObj) 302 | { 303 | $dataDescription = array(); 304 | 305 | // put structs in its own variable that can be accessed independently. 306 | $this->_pcmlStructs = $xmlObj->xpath('struct'); 307 | 308 | // looking for ->data and ->struct. 309 | $dataElements = $xmlObj->xpath('program/data'); 310 | 311 | if ($dataElements) { 312 | foreach ($dataElements as $dataElement) { 313 | 314 | $dataDescription[] = $this->singlePcmlToArray($dataElement); 315 | } 316 | } 317 | 318 | return $dataDescription; 319 | } 320 | } 321 | -------------------------------------------------------------------------------- /ToolkitApi/UserSpace.php: -------------------------------------------------------------------------------- 1 | ToolkitSrvObj = $ToolkitSrvObj ; 20 | } 21 | 22 | return $this; 23 | } 24 | 25 | /** 26 | * @return string 27 | */ 28 | public function getCpfErr() 29 | { 30 | return $this->CPFErr; 31 | } 32 | 33 | /** 34 | * @return mixed 35 | */ 36 | public function getError() 37 | { 38 | return $this->ErrMessage; 39 | } 40 | 41 | // @todo in cw.php, user space api should do an error action with CPF code if needed 42 | /* private function verify_CPFError($retPgmArr, $functionErrMsg) 43 | { 44 | // it's an error if we didn't get output array at all 45 | if (!is_array($retPgmArr)){ 46 | $this->ErrMessage = $functionErrMsg . $this->ToolkitSrvObj->getLastError(); 47 | return true; 48 | } 49 | 50 | $retArr = $retPgmArr['io_param']; 51 | 52 | // get errorDs from named ds (CW style) or directly (PHP toolkit style) 53 | $errorDs = (isset($retArr['errorDs'])) ? $retArr['errorDs'] : $retArr; 54 | // if there's a CPF-style error code 55 | if (isset($errorDs) && ($errorDs['exceptId'] != '0000000')){ 56 | $this->CPFErr = $errorDs['exceptId']; 57 | // @todo future, get actual error text from joblog 58 | $this->ErrMessage = $functionErrMsg . $this->CPFErr; 59 | return true; //some problem 60 | } else { 61 | // no CPF error detected. 62 | $this->CPFErr = '0000000'; 63 | $this->ErrMessage = ''; 64 | return false; 65 | } 66 | 67 | 68 | } //(verify_CPFError) 69 | */ 70 | 71 | /** 72 | * @param null $UserSpaceName 73 | * @param null $USLib 74 | * @param int $InitSize 75 | * @param string $publicAuthority 76 | * @param string $InitValue 77 | * @param string $extendedAttribute 78 | * @param string $textDescription 79 | * @return bool 80 | */ 81 | public function CreateUserSpace($UserSpaceName = NULL, $USLib = NULL, $InitSize =1024, $publicAuthority = '*ALL', $InitValue=' ', 82 | $extendedAttribute='PF', $textDescription='ZS XML Service User Space') 83 | { 84 | // @todo check that 1 <= InitSize <= 16776704 85 | 86 | // set defaults in case blank is passed in 87 | $InitSize = ($InitSize) ? $InitSize : 1024; 88 | $publicAuthority = ($publicAuthority) ? $publicAuthority : '*ALL'; 89 | $InitValue= ($InitValue) ? $InitValue : '00'; // single binary hex value X'00, most efficient initialization, according to documentation of QUSCRTUS 90 | $extendedAttribute = ($extendedAttribute) ? $extendedAttribute : 'PF'; 91 | $textDescription = ($textDescription) ? $textDescription : 'ZS XML Service User Space'; 92 | 93 | // format the user space name and library into 20 char format 94 | $this->setUSName($UserSpaceName, $USLib); 95 | 96 | // format extended attribute into proper format (left-aligned) 97 | $extAttrFormatted = sprintf("%-10s", $extendedAttribute); 98 | 99 | // format authority into proper format (left-aligned) 100 | $authFormatted = sprintf("%-10s", $publicAuthority); 101 | 102 | $params[] = Toolkit::AddParameterChar('in', 20, "USER SPACE NAME", 'userspacename', $this->getUSFullName()); 103 | $params[] = Toolkit::AddParameterChar('in', 10, "Extended Attribute",'extendedattribute', $extAttrFormatted); 104 | $params[] = Toolkit::AddParameterInt32('in', "Initial Size", 'initialsize', $InitSize); 105 | $params[] = Toolkit::AddParameterBin('in', 1, "Initial Value: one byte to fill whole space with", 'initval', $InitValue); 106 | $params[] = Toolkit::AddParameterChar('in', 10, "Public Authority", 'authority', $authFormatted); 107 | $params[] = Toolkit::AddParameterChar('in', 50, "Description", 'description', $textDescription); 108 | $params[] = Toolkit::AddParameterChar('in', 10, "Replace US", 'replaceuserspace', "*NO "); 109 | $params[] = Toolkit::AddErrorDataStruct(); 110 | 111 | // $params = $this->DefineUserSpaceParameters($InitSize, $Auth, $InitChar); 112 | $retPgmArr = $this->ToolkitSrvObj->PgmCall('QUSCRTUS', 'QSYS', $params); 113 | 114 | if ($this->ToolkitSrvObj->verify_CPFError($retPgmArr , "Create user space failed.")) { 115 | return false; 116 | } 117 | 118 | return true; //created 119 | } 120 | 121 | /** 122 | * @return array|bool 123 | */ 124 | public function RetrieveUserSpaceAttr() 125 | { 126 | $BytesRet = 0; 127 | $BytesAv = 25; 128 | $USSize = 0; 129 | $Ext = ' '; 130 | $InitVal = ' '; 131 | $libName = ' '; 132 | 133 | /*Reciever var*/ 134 | $ds[]=Toolkit::AddParameterInt32('out', "Bytes returned", 'ret_bytes', $BytesRet); 135 | $ds[]=Toolkit::AddParameterInt32('out', "Bytes available", 'bytes_avail', $BytesAv); 136 | $ds[]=Toolkit::AddParameterInt32('out', "Space size", 'spacesize', $USSize); 137 | $ds[]=Toolkit::AddParameterChar('out', 1, "Automatic extendibility",'extend_automatic', $Ext); 138 | $ds[]=Toolkit::AddParameterChar('out', 1, "Initial value", 'initval', $InitVal); 139 | $ds[]=Toolkit::AddParameterChar('out', 10, "User space library name", 'uslib', $libName); 140 | //$params[] = array('ds'=>$ds); 141 | $params[] = Toolkit::AddDataStruct($ds, 'receiver'); // note that ds names are discarded 142 | $params[] = Toolkit::AddParameterInt32('in', "Length of reciever",'reciver_len', 24); 143 | $params[] = Toolkit::AddParameterChar('in', 8, "Format name", 'format', "SPCA0100"); 144 | $params[] = Toolkit::AddParameterChar('in', 20, "User space name and library", 'usfullname', $this->getUSFullName()); 145 | $params[] = Toolkit::AddErrorDataStruct(); 146 | 147 | $retPgmArr = $this->ToolkitSrvObj->PgmCall('QUSRUSAT', 'QSYS', $params); 148 | 149 | if ($this->ToolkitSrvObj->verify_CPFError($retPgmArr, "Retrieve user space attributes failed. Error: ")) { 150 | return false; 151 | } 152 | 153 | // If data structure integrity turned off, ds'es are discarded when reading output. The subfields become independent. 154 | // So 'receiver' may not exist. But this changes with data structure integrity, so allow for receiver var. 155 | if (isset($retPgmArr['io_param']['receiver'])) { 156 | // receiver ds var does exist. 157 | $retArr = $retPgmArr['io_param']['receiver']; 158 | } else { 159 | // ds subfields are directly under io_param. 160 | $retArr = $retPgmArr['io_param']; 161 | } 162 | 163 | // return selected values from return array. 164 | return array("Space Size"=>$retArr['spacesize'], 165 | "Automatic extendibility"=> $retArr['extend_automatic'], 166 | "Initial value"=>$retArr['initval'], 167 | "User space library name"=>$retArr['uslib']); 168 | } 169 | 170 | /** 171 | * @return int 172 | */ 173 | public function RetrieveUserSpaceSize() 174 | { 175 | $ret = $this->RetrieveUserSpaceAttr(); 176 | return (isset($ret['Space Size'])? $ret['Space Size']: -1); // -1 is an error condition 177 | } 178 | 179 | /** 180 | * @return bool 181 | */ 182 | public function DeleteUserSpace() 183 | { 184 | $params[] = Toolkit::AddParameterChar('in', 20, "User space name", 'userspacename', $this->getUSFullName()); 185 | $params[] = Toolkit::AddErrorDataStruct(); 186 | 187 | $retPgmArr = $this->ToolkitSrvObj->PgmCall('QUSDLTUS', 'QSYS', $params); 188 | 189 | if ($this->ToolkitSrvObj->verify_CPFError($retPgmArr, "Delete user space failed. Error:")) { 190 | return false; 191 | } 192 | 193 | return true; 194 | } 195 | 196 | /** 197 | * @todo write binary data? 198 | * 199 | * @param int $startpos 200 | * @param $valuelen 201 | * @param $value 202 | * @return bool 203 | */ 204 | public function WriteUserSpace($startpos, $valuelen, $value) 205 | { 206 | //Size ($comment, $varName = '', $labelFindLen = null) { 207 | $params[] = Toolkit::AddParameterChar ('in', 20, "User space name and lib", 'usfullname', $this->getUSFullName()); 208 | $params[] = Toolkit::AddParameterInt32('in', "Starting position",'pos_from', $startpos); 209 | $params[] = Toolkit::AddParameterInt32('in', "Length of data", 'dataLen', $valuelen); 210 | $params[] = Toolkit::AddParameterChar('in', $valuelen, "Input data", 'data_value', $value); 211 | $params[] = Toolkit::AddParameterChar('in', 1, "Force changes to auxiliary storage", 'aux_storage', '0'); 212 | $params[] = Toolkit::AddErrorDataStruct(); 213 | $retPgmArr = $this->ToolkitSrvObj->PgmCall('QUSCHGUS', 'QSYS', $params); 214 | 215 | if ($this->ToolkitSrvObj->verify_CPFError($retPgmArr , "Write into User Space failed. Error:")) { 216 | return false; 217 | } 218 | 219 | return true; 220 | } 221 | 222 | /** 223 | * CW version. $param must be an array of ProgramParameter objects or a single ProgramParameter object. 224 | * 225 | * @param int $startPos 226 | * @param ProgramParameter $param 227 | * @return bool 228 | */ 229 | public function WriteUserSpaceCw($startPos, ProgramParameter $param) 230 | { 231 | /* 232 | if (!is_object($param) && !is_array($param)) { 233 | throw new \Exception('Parameter passed to WriteUserSpaceCw must be an array or ProgramParameter object.'); 234 | } 235 | */ 236 | 237 | // @todo any error, write to toolkit log. 238 | 239 | $labelForSizeOfInputData = 'dssize'; 240 | $param->setParamLabelLen($labelForSizeOfInputData); 241 | //Size ($comment, $varName = '', $labelFindLen = null) { 242 | $params[] = Toolkit::AddParameterChar('in', 20,"User space name and lib", 'usfullname', $this->getUSFullName()); 243 | $params[] = Toolkit::AddParameterInt32('in', "Starting position", 'pos_from', $startPos); 244 | $params[] = Toolkit::AddParameterSize("Length of data",'dataLen', $labelForSizeOfInputData); 245 | $params[] = $param; 246 | $params[] = Toolkit::AddParameterChar('in', 1, "Force changes to auxiliary storage", 'aux_storage', '0'); 247 | $params[] = Toolkit::AddErrorDataStruct(); 248 | 249 | $retPgmArr = $this->ToolkitSrvObj->PgmCall('QUSCHGUS', 'QSYS', $params); 250 | 251 | if ($this->ToolkitSrvObj->getErrorCode()) { 252 | return false; 253 | } else { 254 | return true; 255 | } 256 | } 257 | 258 | /** 259 | * if receiveDescription given, readlen = 0 260 | * 261 | * @param int $frompos 262 | * @param int $readlen 263 | * @param null $receiveStructure 264 | * @return bool 265 | * @throws \Exception 266 | */ 267 | public function ReadUserSpace($frompos=1, $readlen = 0, $receiveStructure = null) 268 | { 269 | //how to see via green screen DSPF STMF('/QSYS.lib/qgpl.lib/ZS14371311.usrspc') 270 | 271 | $dataRead = ' '; 272 | $params[] = Toolkit::AddParameterChar('in', 20, "User space name and library", 'userspacename', $this->getUSFullName()); 273 | $params[] = Toolkit::AddParameterInt32('in', "From position", 'position_from', $frompos); 274 | 275 | $receiverVarName = 'receiverdata'; 276 | 277 | if ($receiveStructure) { 278 | // must be a ProgramParameter 279 | if (!is_object($receiveStructure)) { 280 | throw new \Exception('Parameter 3 passed to ReadUserSpace must be a ProgramParameter object.'); 281 | } 282 | 283 | $labelForSizeOfInputData = 'dssize'; 284 | // 285 | $params[] = Toolkit::AddParameterSize("Length of data", 'dataLen', $labelForSizeOfInputData); 286 | 287 | // wrap simple ds around receive structure so we can assign a varname to retrieve later. 288 | $receiveDs[] = $receiveStructure; 289 | $params[] = Toolkit::AddDataStruct($receiveDs, $receiverVarName, 0, '', false, $labelForSizeOfInputData); 290 | 291 | } else { 292 | // regular readlen, no special structure or size labels. 293 | $params[] = Toolkit::AddParameterInt32('in', "Size of data", 'datasize', $readlen); 294 | $params[] = Toolkit::AddParameterChar('out', $readlen, $receiverVarName, $receiverVarName, $receiveStructure); 295 | } 296 | 297 | $params[] = Toolkit::AddErrorDataStruct(); 298 | 299 | $retPgmArr = $this->ToolkitSrvObj->PgmCall('QUSRTVUS', 'QSYS', $params); 300 | 301 | if ($this->ToolkitSrvObj->verify_CPFError($retPgmArr , "Read user space failed. Error:")) 302 | return false; 303 | $retArr = $retPgmArr['io_param']; 304 | 305 | // return our receiverstructure. 306 | return $retArr[$receiverVarName]; 307 | } 308 | 309 | /* private function DefineUserSpaceParameters($InitSize, $Auth, $InitChar) 310 | { 311 | $params[] = Toolkit::AddParameterChar('in',20, "USER SPACE NAME", 'userspacename', $this->getUSFullName()); 312 | $params[] = Toolkit::AddParameterChar('in',10, "USER TYPE",'userspacetype', "PF "); 313 | $params[] = Toolkit::AddParameterInt32('in', "US SIZE", 'userspacesize', $InitSize); 314 | $params[] = Toolkit::AddParameterChar('in',1, "INITIALIZATION VALUE", 'initval', $InitChar); 315 | $params[] = Toolkit::AddParameterChar('in',10, "AUTHORITY", 'authority', $Auth); 316 | $params[] = Toolkit::AddParameterChar('in',50, "COMMENTS", 'comment', "ZS XML Service"); 317 | $params[] = Toolkit::AddParameterChar('in',10, "Replace US", 'replaceuserspace', "*NO "); 318 | $params[] = Toolkit::AddErrorDataStruct(); 319 | 320 | return $params; 321 | } 322 | */ 323 | 324 | /** 325 | * @return null|string 326 | */ 327 | protected function generate_name() 328 | { 329 | $localtime = localtime(); 330 | $this->USName = sprintf("ZS%d%d%d%d", 331 | $localtime[0],/*sec*/ 332 | $localtime[1],/*min*/ 333 | $localtime[2],/*our*/ 334 | $localtime[3] /*day*/ 335 | ); 336 | return $this->USName; 337 | } 338 | 339 | /** 340 | * @param null $name 341 | * @param null $lib 342 | */ 343 | public function setUSName($name = NULL, $lib = NULL) 344 | { 345 | if ($name === NULL) { 346 | $this->USName = $this->generate_name (); 347 | } else { 348 | $this->USName = $name; 349 | } 350 | 351 | if ($lib === NULL) { 352 | $this->USlib = DFTLIB; 353 | } else { 354 | $this->USlib = $lib; 355 | } 356 | } 357 | 358 | /** 359 | * @return null 360 | */ 361 | public function getUSName() 362 | { 363 | return $this->USName; 364 | } 365 | 366 | /** 367 | * Name and Library 368 | * 369 | * @return null|string 370 | */ 371 | public function getUSFullName() 372 | { 373 | if ($this->USName != null) { 374 | return sprintf("%-10s%-10s", $this->USName, $this->USlib); 375 | } 376 | 377 | return NULL; 378 | } 379 | } 380 | --------------------------------------------------------------------------------