Setting connection charset in Doctrine DBAL with comparison to PHP PDO - php

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);
}
?>

Related

Slim Framework and connection to AS400

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.

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();

PDO with extended PDOStatement cannot disconnect the connection when set NULL

Tested in PHP 5.5.22 and 5.5.25
When using PDO that has extended PDOStatement, MySQL keep connection until when PHP script are finished.
use PDO;
$dbinfoCode = array(
'userid' => 'userid',
'password' => 'password',
'engine' => 'mysql',
'host' => '192.168.100.2',
'database' => 'test',
);
for ($i = 0; $i < 10000; $i++) {
$dsn = sprintf("%s:host=%s;dbname=%s", $dbinfo['engine'], $dbinfo['host'], $dbinfo['database']);
$pdo = new PDO($dsn, $dbinfo['userid'], $dbinfo['password'], $options);
$pdo->setAttribute (PDO::ATTR_STATEMENT_CLASS, array ('PDOStatement2', array($pdo)));
$pdo = null;
}
class PDOStatement2 extends PDOStatement {
}
I can see increasingly stacked "Sleep" processes on MySQL query. Finally, MySQL throw error "Too many connections".
SHOW PROCESSLIST;
If there is no setAttribute about PDO::ATTR_STATEMENT_CLASS, The MySQL connection is disconnected in working order.
$pdo = new PDO($dsn, $dbinfo['userid'], $dbinfo['password'], $options);
//$pdo->setAttribute (PDO::ATTR_STATEMENT_CLASS, array ('PDOStatement2', array($pdo)));
$pdo = null;
I have no idea about it wheather is this a bug or has another solutions.
Finally, I found the solution.
$pdo object on following statement will be duplicated
$pdo->setAttribute (PDO::ATTR_STATEMENT_CLASS, array ('PDOStatement2', array($pdo)));
Use &$pdo instead of $pdo.
$pdo->setAttribute (PDO::ATTR_STATEMENT_CLASS, array ('PDOStatement2', array(&$pdo)));

Yii2 Create Database Connection

I want to create a database connection programmatically without using the config file. I have a form wherein the user enters the database details like the hostname, username, password and the database name. On the basis of these details a new test connection is made and if it passes it should generate the necessary files.
This is what I have tried:
// Create Test DB Connection
Yii::$app->set('id', [
'class' => 'yii\db\Connection',
'dsn' => $dsn,
'username' => $form->username,
'password' => $form->password,
'charset' => 'utf8'
]);
try {
// Check DB Connection
if (Yii::$app->db->getIsActive()) {
// Write Config
$config['components']['db']['class'] = 'yii\db\Connection';
$config['components']['db']['dsn'] = $dsn;
$config['components']['db']['username'] = $username;
$config['components']['db']['password'] = $password;
$config['components']['db']['charset'] = 'utf8';
Configuration::setConfig($config);
$success = TRUE;
return $this->redirect(['init']);
}else{
$errorMsg = 'Incorrect Configurations';
}
} catch (Exception $e) {
$errorMsg = $e->getMessage();
}
I have tested this again and again and even with correct configurations it is giving an error.
All the help is appreciated. Thanks in advance!
You can define a new connection this way:
$db = new yii\db\Connection([
'dsn' => 'mysql:host=localhost;dbname=example',
'username' => 'root',
'password' => '',
'charset' => 'utf8',
]);
$db->open();
After the DB connection is established, one can execute SQL statements like the following eg:
$command = $db->createCommand('SELECT * FROM post');
$posts = $command->queryAll();
// or
$command = $connection->createCommand('UPDATE post SET status=1');
$command->execute();
you can look at this for doc and guide
http://www.yiiframework.com/doc-2.0/guide-db-dao.html
http://www.yiiframework.com/doc-2.0/yii-db-connection.html
I realised my mistake. When using Yii::$app->set() for setting up the db connection, you have to even manually open the connection using Yii::$app->db->open(). Yii doesn't open up the connection for you.

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