zendtech / IbmiToolkit

PHP frontend to XMLSERVICE for IBM i development.
BSD 3-Clause "New" or "Revised" License
46 stars 34 forks source link

Add support for PDO_ODBC and PDO_IBM #99

Closed NoMan2000 closed 4 years ago

NoMan2000 commented 6 years ago

What the name says, it shouldn't be too hard. I've created a wrapper around the class currently to deal with this problem, but it'd be nice if it was supported natively.

NoMan2000 commented 6 years ago

@weierophinney @slaff @clarkphp @adamculp @kaloyan-raev @alanseiden Is this package being maintained or should it be declared dead?

For the ambitious folks, here's the wrapper I wrote that will help out. You need to first write a toolkit wrapper which overwrites one method, the setDb method.


<?php

class ToolkitWrapper extends ToolkitServiceCw
{
    protected function setDb($transportType = '')
    {
        $extensionName = ($transportType) ? $transportType::DBPROTOCOL;
        if (!extension_loaded($extensionName)) {
            throw new \Exception("Extension $extensionName not loaded.");
        }

        if ($extensionName === 'pdo_odbc' || $extensionName === 'pdo_ibm') {
            $this->db = new PdoOdbcSupport();
            $this->setTransport($this->db);
            return;
        }
        parent::setDb($transportType);
    }

Once you have that in place, you write the PdoOdbcSupport class.

<?php

use ToolkitApi\odbcsupp;
use PDO;

class PdoOdbcSupport extends odbcsupp
{
    private $last_errorcode;
    private $last_errormsg;
    private $pdo;

    /**
     *
     * @param $database
     * @param $user
     * @param $password
     * @param null $options
     * @return bool|resource
     */
    public function connect($database = null, $user = null, $password = null, $options = null)
    {
        if ($database instanceof PDO) {
            $pdo = $database;
        } else {
            $options[PDO::ATTR_ERRMODE] = PDO::ERRMODE_EXCEPTION;
            $pdo = new PDO($database, $user, $password, $options);
        }
        $this->setError();
        return $pdo;
    }

    /**
     * set error code and message based on last odbc connection/prepare/execute error.
     *
     * @todo: consider using GET DIAGNOSTICS for even more message text:
     * http://publib.boulder.ibm.com/infocenter/iseries/v5r4/index.jsp?topic=%2Frzala%2Frzalafinder.htm
     *
     * @param null $conn
     */
    protected function setError($conn = null)
    {
        $errors = $this->getPdo()->errorInfo();
        $this->setErrorCode($errors[1]);
        $this->setErrorMsg($errors[2]);
    }

    /**
     * @return PDO
     */
    public function getPdo()
    {
        return $this->pdo;
    }

    /**
     * this function used for special stored procedure call only
     * @example call ZENDSVR6.IPLUGR512K('/tmp/FOOUSER', '*cdata', '<?xml version="1.0" encoding="ISO-8859-1" ?>
     *           <script>
     *           <pgm name="FOO_PROGRAM#" lib="FOO_LIB" >
     *           <parm><data var="INSWIPE" type="32A">%0031000123456?;06982000123456?</data></parm>
     *           <parm><data var="ACCOUNTINF" type="14A"> </data></parm>
     *           <parm><data var="CARDTYP" type="3A"> </data></parm>
     *           <parm><data var="ACCOUNT" type="9p0">0</data></parm>
     *           <parm><data var="SWIPE" type="9A"> </data></parm>
     *           <parm><data var="ERROR" type="10A"> </data></parm>
     *           </pgm>
     *           </script>');
     *
     * @param PDO|null $conn
     * @param $stmt
     * @param $bindArray
     * @throws \Exception
     * @return string
     */
    public function execXMLStoredProcedure($conn, $stmt, $bindArray)
    {
        /**
         * @var \PDO $pdo
         */
        $pdo = $this->pdo;
        try {
            $crsr = $pdo->prepare($stmt);
        } catch (\Exception $e) {
            throw $e;
        }
        if (!$crsr) {
            $this->setError($conn);
            return false;
        }
        // extension problem: sends warning message into the php_log or stdout
        // about number of result sets. (switch on return code of SQLExecute()
        // SQL_SUCCESS_WITH_INFO
        if (!@$crsr->execute([
            $bindArray['internalKey'],
            $bindArray['controlKey'],
            $bindArray['inputXml']
        ])
        ) {
            $this->setError($pdo );
            return "ODBC error code: " . $this->getErrorCode() . ' msg: ' . $this->getErrorMsg();
        }

        $row = '';
        $outputXML = '';
        if (!$bindArray['disconnect']) {
            while ($tmp = $crsr->fetchColumn(0)) {
                if ($tmp) {
                    if (strstr($tmp, "</script>")) {
                        $pos = strpos($tmp, "</script>");
                        $pos += strlen("</script>"); 
                        $row .= substr($tmp, 0, $pos);
                        break;
                    } else {
                        $row .= $tmp;
                    }
                }
            }
            $outputXML = $row;
        }
        return $outputXML;
    }

    /**
     * @param $conn
     * @param $stmt
     * @return array
     */
    public function executeQuery($conn, $stmt)
    {
        $txt = [];
        $crsr = $this->getPdo()->query($stmt);

        if ($crsr) {
            while ($row = $crsr->fetchColumn(0)) {
                if (!$row) {
                    break;
                }

                $txt[] = $row;
            }
        } else {
            $this->setError($conn);
        }

        return $txt;
    }
}
clarkphp commented 6 years ago

Hi Noman2000. PDO_ODBC, PDO_IBM, and the PHP IBM i Toolkit are definitely not dead.

alanseiden commented 4 years ago

https://github.com/zendtech/IbmiToolkit/commit/1e0e8fcc2da9c7c37d23eec4716003da82cb9d15