Slim Framework and connection to AS400 - php

I tried to connect to AS400 using Php Slim Framework.
It returned to me an error about odbc connection php function.
I edited this framework files this way.
SETTINGS.PHP
<?php
declare(strict_types=1);
use App\Application\Settings\Settings;
use App\Application\Settings\SettingsInterface;
use DI\ContainerBuilder;
use Monolog\Logger;
return function (ContainerBuilder $containerBuilder) {
// Global Settings Object
$containerBuilder->addDefinitions([
SettingsInterface::class => function () {
return new Settings([
'displayErrorDetails' => true, // Should be set to false in production
'logError' => false,
'logErrorDetails' => false,
'logger' => [
'name' => 'slim-app',
'path' => isset($_ENV['docker']) ? 'php://stdout' : __DIR__ . '/../logs/app.log',
'level' => Logger::DEBUG,
],
"db" => [
'name' => 'EDDXXXXXXX',
'username' => 'XXXXXXX',
'password' => 'XXXXXXXX',
'connection' => 'xxxxxx.xxx.xxxxx'
]
]);
}
]);
};
DEPENDENCIES.PHP
<?php
declare(strict_types=1);
use App\Application\Settings\SettingsInterface;
use DI\ContainerBuilder;
use Monolog\Handler\StreamHandler;
use Monolog\Logger;
use Monolog\Processor\UidProcessor;
use Psr\Container\ContainerInterface;
use Psr\Log\LoggerInterface;
return function (ContainerBuilder $containerBuilder) {
$containerBuilder->addDefinitions([
LoggerInterface::class => function (ContainerInterface $c) {
$settings = $c->get(SettingsInterface::class);
$loggerSettings = ...........CODE HERE ..........
return $logger;
},
PDO::class => function (ContainerInterface $c) {
$settings = $c->get(SettingsInterface::class);
$dbSettings = $settings->get('db');
$name = $dbSettings['name'];
$username = $dbSettings['username'];
$password = $dbSettings['password'];
$dsn = "Driver={Client Access ODBC Driver (32-bit)};System=" .
$connection . ";libraries=" . $name .
";naming=system;transaction isolation=read committed;Uid=" .
$username .";Pwd=" . $password . ";";
//return new PDO($dsn, $username, $password);
return odbc_connect($dsn, $username, $password);
},
]);
};
ROUTES.PHP
$app->get('/db-test', function (Request $request, Response $response) {
$db = $this->get(PDO::class);
$sth = $db->prepare("SELECT * FROM XXXX");
$sth->execute();
$data = $sth->fetchAll(PDO::FETCH_ASSOC);
$payload = json_encode($data);
$response->getBody()->write($payload);
return $response->withHeader('Content-Type', 'application/json');
});
When i call /db-test I obtain this server error
"statusCode": 500,
"error": {
"type": "SERVER_ERROR",
"description": "ERROR: odbc_connect(): SQL error: [Microsoft][Driver Manager ODBC] Nome origine dati non trovato e driver predefinito non specificato., SQL state IM002 in SQLConnect on line 46 in file C:\\slim\\as400\\app\\dependencies.php."
}
I don't understand why it gives to me that error.

The odbc_connect function in your DI container is not correct, because it returns a resource, but not a PDO object. It should be return new PDO($dsn, $username, $password);
Data source name not found and default driver not specified., SQL state IM002. Make sure that the ODBC driver is installed and that the data-source name exists. I recommend to try this connection string for AS400.
Provider=IBMDA400;Data Source=MY_SYSTEM_NAME;User Id=myUsername; Password=myPassword;Default Collection=MY_LIBRARY;
or
Driver={IBM i Access ODBC Driver};System=mySystem;Uid=myUser;Pwd=myPassword;
File DSN for iSeries AS400 ODBC connection
https://www.connectionstrings.com/as-400/

My experience with PHP and IBM i is that you need to use the ODBC drivers, not PDO_ODBC. This experience is admittedly old, and may no longer be true, however, I looked at the current PHP 8.1 documentation for PDO_ODBC and found this:
ibm-db2
Supports access to IBM DB2 Universal Database, Cloudscape, and Apache Derby servers through the free DB2 express-C client.
unixODBC
Supports access to database servers through the unixODBC driver manager and the database's own ODBC drivers.
generic
Offers a compile option for ODBC driver managers that are not explicitly supported by PDO_ODBC.
On Windows, php_pdo_odbc.dll has to be enabled as extension in php.ini. It is linked against the Windows ODBC Driver Manager so that PHP can connect to any database cataloged as a System DSN.
So PDO_ODBC supports DB2 UDB. This is only one of the three flavors of DB2, and unfortunately not the one used by IBM i. So maybe you can get the generic ODBC PDO to work, but I would stick to just ODBC, not PDO unless you execute the spend money command and purchase the DB2 Connect product which will let you access the enterprise DB2 products (DB2 for i and DB2 for zOS) from a Windows/Unix/Linux box.

Related

how to connect to mongodb database in php 7

I am using mongodb 3.2 and php 7
I have installed the driver and its working..
Here is my code
<?php
$client = new MongoDB\Driver\Manager();
$db = $client->selectDatabase('inventory');
?>
how to connect to database "inventory"
the error that comes is
Fatal error: Uncaught Error: Call to undefined method MongoDB\Driver\Manager::selectDatabase()
I don't think you want to call the Manager directly. The newer MongoDB extension that replaces the built-in PHP Mongo DB client; You need to have the following requirements for the code to work. The code example that follows assumes the following:
You load with the composer autoloader.
You have the new MongoDB Driver extension from here: http://php.net/manual/en/set.mongodb.php
That you use the composer MongoDB Client library from here: http://php.net/manual/en/mongodb.tutorial.library.php
PHP >=5.6
use MongoDB\Client as MongoDbClient;
// When auth is turned on, then pass in these as the second parameters to the client.
$options = [
'password' => '123456',
'username' => 'superUser',
];
try {
$mongoDbClient = new MongoDbClient('mongodb://localhost:27017');
} catch (Exception $error) {
echo $error->getMessage(); die(1);
}
// This will get or make (in case it does not exist) the inventory database
// and a collection beers in the Mongo DV server.
$collection = $mongoDbClient->inventory->beers;
$collection->insertOne( [ 'name' => 'Hinterland', 'brewery' => 'BrewDog' ] );
$result = $collection->find( [ 'name' => 'Hinterland', 'brewery' => 'BrewDog' ] );
foreach ($result as $entry) {
echo $entry['_id'], ': ', $entry['name'], "\n";
}

DBAL connect immediately

In PDO (And likewise DBAL) if there's a problem authenticating with the server it will throw an exception containing a trace including the database username and password.
Needless to say, this is a problem, and in PDO I would wrap it in a try block and re-throw the error without the stack trace. Problem solved.
However, DBAL doesn't actually initiate the connection until the first query is called, so it misses the try block completely and spews my database credentials the first chance it gets!
How can I force DBAL to connect immediately so I can catch any authentication errors?
\Doctrine\DBAL\Connection::connect()
Obvious in retrospect.
My database is ibm db2 so I use the odbc pdo connection with dbal. This is how I set up my connection, notice that the PDO is in a try catch block
use Doctrine\DBAL\Configuration;
use Doctrine\DBAL\DriverManager;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Query\QueryBuilder;
$connPdo = null;
try {
$connPdo = new PDO('odbc:'.SYSTEM_DSN_NAME, DB_USERNAME, DB_PASSWORD, array(
PDO::ATTR_PERSISTENT => TRUE,
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION)
);
} catch (PDOException $e) {
echo $e->getMessage() . '</br>';
}
$config = new Configuration();
$connectionParams = array(
'user' => DB_USERNAME,
'password' => DB_PASSWORD,
'pdo' => $connPdo,
'driverClass' =>'Doctrine\DBAL\Driver\PDOIbm\Driver',
);
$connection = DriverManager::getConnection($connectionParams, $config);
$queryBuilder = $connection->createQueryBuilder();

PHP ZF2 Mysql has gone away

Hi I have a php daemon that handle request from rabbitmq
After a day, it can no longer execute due to error MySQL has gone away.
PHP Warning: PDOStatement::execute(): MySQL server has gone away in /var/www/daemon/www/vendor/zendframework/zendframework/library/Zend/Db/Adapter/Driver/Pdo/Statement.php on line 239
PHP Warning: PDOStatement::execute(): Error reading result set\'s header in /var/www/daemon/www/vendor/zendframework/zendframework/library/Zend/Db/Adapter/Driver/Pdo/Statement.php on line 239
I didn't use doctrine, instead I send my \Zend\Db\Adapter\Adapter to a db wrapper class with below function.
public static function executeScalar($statement, $parameters, \Zend\Db\Adapter\Adapter $dbAdapter)
{
$dbResult = new DbResult();
if (! $statement) {
$dbResult->addError('No statement given');
return $dbResult;
}
$stmt = $dbAdapter->createStatement();
$stmt->prepare($statement);
foreach ($parameters as $key => &$param) {
$stmt->getResource()->bindParam($key + 1, $param[0], $param[1]);
}
try {
$result = $stmt->execute();
$dbResult->setResult($result);
} catch (\Zend\Db\Adapter\ExceptionInterface $e) {
$dbResult->addError('DB Error');
$message = $e->getPrevious() ? $e->getPrevious()->getMessage() : $e->getMessage();
$dbResult->addError($message);
} catch (\Zend\Db\Adapter\Exception $e) {
$dbResult->addError('DB Error');
$dbResult->addError($e->getMessage());
} catch (\PDOException $e) {
$dbResult->addError('DB Error');
$dbResult->addError($e->getMessage());
} catch (\Exception $e) {
$dbResult->addError('DB Error');
$dbResult->addError($e->getMessage());
}
$stmt->getResource()->closeCursor();
return $dbResult;
}
DbResult is my own db result wrapper class it mainly check whether it return empty, what's the error, how many rows, etc.
Here is my database.local.php configuration
return array(
'service_manager' => array(
'factories' => array(
'mysql' => function ($sm)
{
return new Zend\Db\Adapter\Adapter(array(
'driver' => 'PdoMysql',
'hostname' => 'localhost',
'database' => 'daemon',
'username' => 'daemon',
'password' => 'password',
'driver_options' => array(
\PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES \'UTF8\'',
\PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION,
\PDO::ATTR_EMULATE_PREPARES => true,
\PDO::MYSQL_ATTR_LOCAL_INFILE => true
)
));
},
)
)
)
So everytime I want to execute a sql I do this inside controller or any other class ( Just an example )
$service = $this->getServiceLocator();
$dbAdapter = $service->get('mysql');
$get = \Db\Database::executeScalar('SELECT * FROM mytable WHERE id <= ?', array(10), $dbAdapter);
It seems I cannot catch the warning, and is there a way to force reconnect or perhaps I just do a disconnect after each request ?
Will this works, to handle the error ?
On every new request I do this
$dbAdapter->getDriver()->getConnection()->connect();
At the end of request I do this
$dbAdapter->getDriver()->getConnection()->disconnect();
Yes, I check the persistent connection option, but I also not fond of it.
I find the problem, it cause by mysql server close idle connection after 'wait timeout'. when mysql closing the idle connection, PDO will not receive any event so the next time you initiate a query it will return Mysql has gone away error.
For http request this is acceptable since after the server response the request it will stop/exit php execution which close all connection to database.
For daemon/service this is not acceptable since most of the time it waiting for client request (idle). My solution is to close the connection everytime it finish handling client request. e.g :
while (true) {
//listen to rabbitmq queue
//...
//do something base on client request from rabbitmq queue
//...
//close the connection whether it use database or not
//connection will be reconnected when we call $service->get('mysql');
$service = $this->getServiceLocator();
$dbAdapter = $service->get('mysql');
$dbAdapter->getDriver()->getConnection()->disconnect();
}
You can create a persistent connection to your database but be warned that creating a persistent connection should not be the first solution to look for. Be sure to do some research on the subject before trying it. You can find some documentation here :
http://php.net/manual/en/pdo.connections.php#example-954
On the other hand, you should look for the queries sent so the reason of the gone away message is not caused by the mysql server recieving a packet too large (ex: inserting a large blob). Because if it is, the connection will still close unexpectedly.

Setting connection charset in Doctrine DBAL with comparison to PHP PDO

Are there any needs for using set names ourcharset with DBAL with PHP >=5.3.2 and <5.3.6?
Prior to PHP 5.3.6, the charset option in PDO connection was ignored.
If we were running an older version of PHP, we had to use set names ourcharset.
Actual Doctrine DBAL 2.5.1 require PHP >=5.3.2.
I can't find what Doctrine team is advising if someone have PHP <5.3.6 version.
DBAL is mostly based on PDO, but it also have some improvements over it, so I was thinking maybe this was improved... but at Doctrine DBAL documentation page I have found only this:
Up until PHP 5.3.6 PDO has a security problem when using non ascii
compatible charsets. Even if specifying the charset using “SET NAMES”,
emulated prepared statements and PDO#quote could not reliably escape
values, opening up to potential SQL injections. If you are running PHP
5.3.6 you can solve this issue by passing the driver option “charset” to Doctrine PDO MySQL driver. Using SET NAMES does not suffice!
In PDO to this time I have done:
<?php
$dsn = 'mysql:host='.$_SESSION['options']['host'].';port='.$_SESSION['options']['port'].';dbname='.$_SESSION['options']['dbname'].';charset='.$_SESSION['options']['charset'];
try {
$conn = new \PDO($dsn, $_SESSION['options']['user'], $_SESSION['options']['pass']);
if(version_compare(PHP_VERSION, '5.3.6', '<')) //is this required with DBAL?
$conn->exec("set names {$_SESSION['options']['charset']}");
} catch (\PDOException $e) {
trigger_error($e->getMessage(), E_USER_ERROR);
}
?>
With DBAL it's:
<?php
require_once "lib/autoload.php";
$config = new \Doctrine\DBAL\Configuration();
$params = array(
'dbname' => $_SESSION['options']['dbname'],
'user' => $_SESSION['options']['user'],
'password' => $_SESSION['options']['pass'],
'host' => $_SESSION['options']['host'],
'port' => $_SESSION['options']['port'],
'driver' => 'pdo_mysql',
'charset' => $_SESSION['options']['charset'],
);
try {
$conn = \Doctrine\DBAL\DriverManager::getConnection($params, $config);
} catch (\Exception $e) {
trigger_error($e->getMessage(), E_USER_ERROR);
}
?>
It looks that DBAL do not improved here anything.
So, if there is possibility that our app will used with PHP between >=5.3.2 and <5.3.6, then yes, use additional SET NAMES:
<?php
require_once "lib/autoload.php";
$config = new \Doctrine\DBAL\Configuration();
$params = array(
'dbname' => $_SESSION['options']['dbname'],
'user' => $_SESSION['options']['user'],
'password' => $_SESSION['options']['pass'],
'host' => $_SESSION['options']['host'],
'port' => $_SESSION['options']['port'],
'driver' => 'pdo_mysql',
'charset' => $_SESSION['options']['charset'],
);
if(version_compare(PHP_VERSION, '5.3.6', '<'))
$params['driverOptions'] = array(1002=>'SET NAMES '.$_SESSION['options']['charset']);
//"1002" is value of constant MYSQL_ATTR_INIT_COMMAND
try {
$conn = \Doctrine\DBAL\DriverManager::getConnection($params, $config);
} catch (\Exception $e) {
trigger_error($e->getMessage(), E_USER_ERROR);
}
?>

PHP pdo exception "could not find driver" pgsql

So i always get a pdo exception when i try to create my db in the constructor of my Mapper class.
on this line:
$this->db = new PDO($dsn, $db_config['username'], $db_config['password']);
this is my dsn creation:
$db_config = array(
'driver' => 'pgsql',
'username' => $dbUser,
'password' => $dbPassword,
'schema' => 'r0628740',
'dsn' => array(
'host' => 'gegevensbanken.khleuven.be',
'dbname' => '2TX31',
'port' => '51314',
)
);
and finaly my constructor:
public function __construct(){
global $db_config;
$dsn = $db_config['driver'] . ':';
foreach($db_config['dsn'] as $key => $value){
$dsn .= $key . '=' . $value . ';';
}
try{
$this->db = new PDO($dsn, $db_config['username'], $db_config['password']);
$this->db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
if(($db_config['driver'] == 'pgsql') && isset($db_config['schema'])){
$this->db->query(sprintf("SET SEARCH_PATH TO %s"), $db_config['schema']);
}
}catch (PDOException $e){
var_dump($e->getLine());
error_log($e->getMessage());
}
}
The PHP contains a dll required by pgsql and pgsql_pdo driver libpq.dll...
Add PHP binary path to system path or copy de DLL into Windows\system32. On linux dependency are installed automatically.
I found article somewhere, works for me. Assuming you have installed PostgreSQL and your WAMP installation is on c:\wamp, you will need to copy:
c:\wamp\bin\php\php5.3.9\libpq.dll to c:\wamp\bin\apache\Apache2.2.11\bin.
Make sure you also have the following files:
C:\wamp\bin\php\php5.3.9\ext\php_pdo_pgsql.dll and
C:\wamp\bin\php\php5.3.9\ext\php_pgsql.dll
Also, make sure you have enabled the above 2 files as extensions, either via the WAMP menu (click on WAMP icon on taskbar: PHP > PHP extensions, find the above 2 and 'check' them).
Please note that php5.3.9 and Apache2.2.11 refer to my specific PHP and Apache versions.
Adjust those to suit your installation.
Copying this libpq.dll from c:\wamp\bin\php\php5.3.9\libpq.dll to c:\wamp\bin\apache\Apache2.2.11\bin. has worked for me

Categories