Symfony 1.4 connect to mysql via SSL - php

I need to change an old Symfony 1.4 application so that it's able to connect to mysql via ssl-connection.
I found a lot about this for Symfony >= 2. But unfortunately not for this dusty one.
For validation purposes I already made it work by editing
./apps/frontend/lib/vendor/symfony/lib/plugins/sfDoctrinePlugin/lib/vendor/doctrine/Connection.php
$this->dbh = new PDO($this->options['dsn'], $this->options['username'],
(!$this->options['password'] ? '':$this->options['password']), array(PDO::ATTR_PERSISTENT => true));
to
$this->dbh = new PDO($this->options['dsn'], $this->options['username'],
(!$this->options['password'] ? '':$this->options['password']),
array(PDO::ATTR_PERSISTENT => true,
PDO::MYSQL_ATTR_SSL_KEY => '/etc/my.cnf.d/ssl/client-key.pem',
PDO::MYSQL_ATTR_SSL_CERT => '/etc/my.cnf.d/ssl/client-cert.pem',
PDO::MYSQL_ATTR_SSL_CA => '/etc/my.cnf.d/ssl/ca-cert.pem'));
But I wonder if this ugly hack is actually the only solution?

It took me a while to see that this connection class is already overwritten (apps/frontend/lib...).
So I only had to make these variables configurable. There is an option in databases.yml configuration called attributes (doctrine::param::attributes). If you pass non-string keys you can get them with getAttribute.
So at least it works (it's inside the try area of connect-method).
$sslOptionKeys = array(PDO::MYSQL_ATTR_SSL_KEY, PDO::MYSQL_ATTR_SSL_CERT, PDO::MYSQL_ATTR_SSL_CA);
foreach($sslOptionKeys as $sslOptionKey) {
if(array_key_exists($sslOptionKey, $this->pendingAttributes)) {
$pdoOptions[$sslOptionKey] = $this->getAttribute($sslOptionKey);
}
}
$this->dbh = new PDO($this->options['dsn'], $this->options['username'],
(!$this->options['password'] ? '':$this->options['password']),
$pdoOptions);
In databases.yml you will have to type the following (comments help to understand these numbers)
all:
doctrine:
class: sfDoctrineDatabase
param:
dsn: mysql:host=localhost;dbname=db
username: user
password: pass
encoding: utf8
attributes:
#PDO::MYSQL_ATTR_SSL_KEY
1010: /etc/my.cnf.d/ssl/client-key.pem
#PDO::MYSQL_ATTR_SSL_CERT
1011: /etc/my.cnf.d/ssl/client-cert.pem
#PDO::MYSQL_ATTR_SSL_CA
1012: /etc/my.cnf.d/ssl/ca-cert.pem

We found that the attributes array was not working. We had to add an event listener that listened for the doctrine 'doctrine.configure_connection' event and set the properties on the connection directly.
class ProjectConfiguration extends sfProjectConfiguration
{
public function setup()
{
//existing code
$this->dispatcher->connect('doctrine.configure_connection', array(
'ProjectConfiguration','addConnectionSSL'
));
}
static public function addConnectionSSL(sfEvent $event){
$connection = $event->getParameters()['connection'];
/* #var $connection Doctrine_Manager */
$other = $connection->getOption('other');
if(!is_array($other)) $other=array();
$other[PDO::MYSQL_ATTR_SSL_CA] = "PATH_TO_CERT_FILE"; //Set this to actual path. You can also set other properties in the same way.
$connection->setOption('other',$other);
}
}

Related

PHP database class - Managing two connections with different privileges

I'm just trying to master the art of using classes in PHP and have come across a concern.
For security reasons, I sometimes use two database connections in my application; one with read-only privileges and one with full read/write. Unfortunately, I wasn't really thinking about this when I started to build a few classes.
So, I have a database class, which is essentially a pointless PDO wrapper (pointless because PDO is a wrapper), but thought it'd be good practice to write one anyway and I may extend PDO later too. This is what I did:
<?php
class Database {
private $dbh;
public function __construct($accessLevel = NULL, $credentials = NULL) {
if (gettype($credentials) === 'array') {
$dsn = 'mysql:host='.$credentials['dbHost'].';dbname='.$credentials['dbName'];
$options = array (
PDO::ATTR_PERSISTENT => false,
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
);
if ($accessLevel == "public") {
$this->dbh = new PDO($dsn, $credentials['publicUser'], $credentials['publicPassword'], $options);
} else if ($accessLevel == "private") {
$this->dbh = new PDO($dsn, $credentials['privateUser'], $credentials['privatePassword'], $options);
}
}
}
// other database functions
}
?>
For a public connection (read/write), I simply used this:
$db = new Database('public', config['dbConfig']);
... or for a private connection (read-only), I used this:
$db = new Database('private', config['dbConfig']);
... and when I wanted to use the connection in another class, I simply injected the connection, like so:
$user = new User($db);
Works fine, but then I realised that I may need two connections inside that class, one for reading only and one for all. So I got a little confused, but had a go regardless. This is what I did:
Instead of calling the class with either of the connections, I called the class twice with both, like so:
$publicDB = new Database('public', $config['db']);
$privateDB = new Database('private', $config['db']);
and then injected both of those to my other class:
$user = new User($publicDB, $privateDB);
Inside this User class, this how I used the two connections:
<?php
class User {
private $dbh;
private $dbhp;
public function __construct($publicDatabase = NULL, $privateDatabase = NULL) {
$this->dbh = $publicDatabase;
$this->dbhp = $privateDatabase;
}
public function doSomething() {
$this->dbh->query("INSERT INTO......");
$this->dbh->execute();
}
public function doSomethingSafely() {
$this->dbhp->query("SELECT * FROM......");
$results = $this->dbhp->resultset();
}
}
?>
Right, this actually works fine but I'm worried it's not the acceptable method or it may cause problems later down the road. I have a few questions:
Is using two connections with different privileges still considered good practice? Even though I'm properly escaping and validating my data and binding the values using PDO?
If yes for above, is there a better way to manage the two connections for using in many classes or is what I have done acceptable?

New to OOP PHP, guidance request, data sharing between classes design

Writing my first application in PHP that makes use of classes and objects.
I have a DB class which takes a string to select the appropriate config because there are multiple databases. I started out with a login class but scratched that idea in exchange for a user class so I can do user->isLoggedIn stuff. The user class uses MySQL which stores user and login information, as well as credentials for the second database.
$mysql = new db( 'mysql' );
$user = new user( $mysql );
if( !( $user->isLoggedIn() === true ) )
{
goToPage( 'login.php' );
exit();
}
The second database is Sybase and stores account information. I need the credentials from the user class to get lists and accounts information from here. I think an account class should be next but not sure of the best way to do it. Something like this maybe..
$sybase = new db( 'sybase' );
$account = new account( $sybase );
$account_list = $account->getList( $user->info );
$user->info being an array i guess of credentials and info needed for the account table, or is there a better way to go about this?
Edited to include db class example
config.db.php
$config_array = array();
$config_array[ 'mysql' ][ 'dsn' ] = "mysql:host=localhost;dbname=********;charset=UTF-8";
$config_array[ 'mysql' ][ 'user' ] = "********";
$config_array[ 'mysql' ][ 'pass' ] = "**************";
$config_array[ 'sybase' ][ 'dsn' ] = "odbc:DRIVER={Adaptive Server Anywhere 8.0};***************";
$config_array[ 'sybase' ][ 'user' ] = "**********";
$config_array[ 'sybase' ][ 'pass' ] = "*********";
class.db.php
public function __construct( $type )
{
require 'config.db.php';
$this->type = $type;
foreach( $config_array[ $this->type ] AS $key => $value )
{
$this->$key = $value;
}
try
{
$this->connection = new PDO( $this->dsn, $this->user, $this->pass, $this->options );
}
catch( PDOException $ex )
{
log_action( "db->" . $this->type, $ex->getCode() . ": " . $ex->getMessage() );
$this->error = true;
}
return $this->connection;
}
Does something like the following make sense?
class User()
{
public function getAccountList()
{
$this->Account = new Account( new Database( 'sybase' ) );
return $this->Account->list( $this->userid );
}
}
Also, Accounts have different sections (i.e. History & Notes, Financial Transactions, Documents) that will be different 'tabs' on the page. Should each one of those be a class too?
First up, as a secondary answer on the other answer by Dimitry:
The things you'll gain by having a Singleton pale to the things you lose. You get a global object, which every part of your program can read and modify, but you lost testability, readability, and maintainability.
Because the Singleton object is in fact global, you cannot accurately isolate the single units in your application (which is crucial for Unit Testing), because your function is dependant on other components, which are "magically" inserted into it.
You lose readability because method() may actually need other things to work (for instance, the user object needs a db instance, this makes new user($db) much more readable than new user(), because even without looking at the source code, I can tell what the method/object needs to work.
You lose maintainability because of the reason stated above as well. It's harder to tell which components are inserted via the Singleton than it is to see it in the function's "contract", for that reason, it'll be harder for future you and/or any other developer to read and refactor your code.
As a side note, it's considered good naming convention to name Class names with its first letter upper cased, I'll be using this convention from now on.
Let's start from the top. You have 2 possible states for your Db object, MysqlDb, and SybaseDb. This calls for polymorphism. You have an abstract class Db, and two concrete classes MysqlDb and SybaseDb inheriting from it. The instantiation of the correct Db object is the responsibility of a factory
class DbFactory {
private $db;
/**
* Create a new Db object base on Type, and pass parameters to it.
*
* #param string $type Type of database, Mysql or Sybase.
* #param string $dsn The DSN string corresponding to the type.
* #param string $user User credential
* #param string $pass Password credential
*
* #return Db
*/
public function create($type, $dsn, $user, $pass) {
if (!is_a($this->db, "Db")) {
$type = $type . "Db";
$this->db = new $type($dsn, $user, $pass);
}
return $this->db;
}
}
abstract class Db {
/**
* #param $dsn
* #param $user
* #param $pass
*/
public function __construct($dsn, $user, $pass) {
$this->db = new PDO("$dsn", $user, $pass);
$this->db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$this->db->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
}
}
class MysqlDb extends Db {
/*
* More specific, Mysql only implementation goes here
*/
}
class SybaseDb extends Db {
/*
* More specific, Sybase only implementation goes here
*/
}
Now you should ask yourself, who is responsible for getting the list of accounts? Surely they shouldn't fetch themselves (just as much as it isn't the user's responsibility to fetch its own data from the database). It's the User's responsibility to fetch these accounts, using the SybaseDb connection.
In fact, the User needs the following to work:
Sybase database connection - to fetch Account list. We'll pass the DbFactory instance to it, so that we can easily get the instance if it were already instantiated, and instantiate it if not.
State information (logged in status, user ID, user name, etc).
The User isn't responsible to set this data, it needs it in order to work.
So the User constructor should look like this (assuming "ID" and "name" fields):
User::__construct($id, $name, DbFactory $factory);
The User will have a field $accounts, which would hold an array of Account objects. This array would be populated with a User::getAccounts() method.
I'd make Account the member of User class. "User has an Account", right?
Also, speaking about classes, make sure that your two DBs are singletones, and get them via getInstance(), not __construct().

Is there zend bootstrap analog in yii?

I want to try db connection to check is db available. In zend I can place my code in boostrap file and wrap it in try catch.
How to implement this in yii?
Is in yii analog of zend boostrap?
UPD: db is mongo, yii extention for working with db is a directmongosuite
Seems that I find appropriate solution:
Need to prohibit auto connect in config file:
'components' => array(
'edms' => array(
'class' => 'EDMSConnection',
'dbName' => 'homeweb',
'server' => 'mongodb://localhost:27017',
'options' => array('connect' => false)
)
)
all controllers should extend one custom controller (BaseController for example).
Need to write own public function beforeAction method where I can add boostrap code.
class BaseController extends CController
{
public $layout = '//layouts/main';
public $navigationMenu = array();
public $breadcrumbs = array();
public function beforeAction($action)
{
try {
Yii::app()->edmsMongo()->connect();
} catch (Exception $e) {
die('Cannot connect to the database server. Please Try again later.');
}
$isGuest = Yii::app()->user->isGuest;
$this->navigationMenu = $this->_getNavigationMenu($isGuest);
return parent::beforeAction($action);
}
In the beforeAction method need to add return true or execute parent's method.
The bootstrap in yii is pretty much the index.php file under public_html or the yiic.php file (for command line applications).
You will probably have to separate the creating of the application instance and running it (by default it does both on 1 line), so you can do your try/catch between the calls.
Just try to fetch the app component, the mongo plugin will throw an exception if it can't open the connection:
try
{
Yii::app()->mongoDb;
}
...
or Yii::app()->getComponent('mongoDb');

Unable to access variable from included file

As the title says there is a problem accessing variable (associative array) inside class from included file. Here is the source code both class and include file:
require("applications/cw_database.php");
require("config/dbConfig.php");
require("config/appConfig.php");
class APP_ASSESMENTS
{
private $dbObj;
private $DisplayOutput = "";
public function __construct($PageParams)
{
try
{
$dbObj = new CW_DB($dbConfig['hostname'],$dbConfig['username'],$dbConfig['password'],$dbConfig['name'],$dbConfig['port']);
} catch (Exception $e) {
throw new ErrorException($e);
}
}
...
The other part has nothing to do with $dbConfig.
Also this is the included file (config/dbConfig.php):
/*
Testing configuration for MySQL database
*/
$dbConfig['username'] = "phpcoursework"; // changed on demand
$dbConfig['password'] = "phpcoursework"; // changed on demand
$dbConfig['hostname'] = "localhost"; // changed on demand
$dbConfig['name'] = "students"; // changed on demand
$dbConfig['port'] = 3306; // default for MySQL
First, $dbObj will not automatically assume class member scope, it will create a local copy of CW_DB and discard it when __construct returns. You need to explicitly reference the property;
$this->dbObj = ...
Anyway, global state using global as suggested by others will "work", but if you're using OOP practices you're best not to do that. You can actually return from an include(), so an option would be to do the following:
// your config file dbConfig.php
return array(
'username' => "phpcoursework",
'password' => "phpcoursework",
'hostname' => "localhost",
'name' => "students",
'port' => 3306,
);
And inject it into the object, via constructor or method (here's constructor)
class APP_ASSESMENTS
{
private $dbObj;
public function __construct($dbConfig, $PageParams)
{
$dbObj = new CW_DB($dbConfig['hostname'], $dbConfig['username'],
$dbConfig['password'], $dbConfig['name'], $dbConfig['port']);
// ...
}
}
// include() here, will actually return the array from the config file
$appAssesments = new \APP_ASSESMENTS(include('dbConfig.php'), $PageParams);
It would be recommended that you go another level up: instead, inject the database object itself, taking the dependency out of your APP_ASSESSMENTS class.
(Also, PascalCase is the typical convention of class naming, such as AppAssessments and CwDb)
$dbObj = new CwDb(include('dbConfig.php'));
$appAssessments = new AppAssessments($dbObj, $etc, $etc);
This simple change allows you to remove the dependency from AppAssessments on CwDb. That way, if you extend CwDb for some reason, you can just pass in an instance of the extended class without having to change any code in AppAssessments
You can change the AppAssessments constructor like so:
public function __construct(CwDb $db, $etc, $etc){
$this->db = $db;
// ...
}
This takes advantage of PHPs (limited, albeit still useful) type-hinting, ensuring the first argument is always of the correct type.
This plays into part of the open/closed principle: classes should be open to extension but closed for modification.
Includes are used in the scope of access. So, to access the variables you need to include the files within your class. As earlier mentioned global will let you access variables from another scope too. But global should be used with caution! See the documentation.
See the manual for more information.
Edit: I need to make it clear that globals are never a good alternative for handling these kind of critical variables..
public function __construct($PageParams){
global $dbConfig;
try{
$dbObj = new CW_DB($dbConfig['hostname'],$dbConfig['username'],$dbConfig['password'],$dbConfig['name'],$dbC onfig['port']);
} catch (Exception $e) {
throw new ErrorException($e);
}
}
or you could use $GLOBALS['dbConfig'].

make db connection persistent throught zend framework

I'm using zend framework. currently everytime I need to use the db I go ahead and connect to the DB:
function connect()
{
$connParams = array(
"host" => $host,
"port" => $port,
"username" => $username,
"password" => $password,
"dbname" => $dbname
);
$db = new Zend_Db_Adapter_Pdo_Mysql($connParams);
return $db
}
so I would just call the connect() function everytime I need to use the db
My question is...suppose I want to reuse $db everywhere in my site and only connect once in the very initial stage of the site load and then close the connection right before the site gets sent to the user, what would be the best practice to accomplish this?
Which file in Zend should I save $db in, what method should I use to save it (global variable?), and which file should I do the connection closing in?
If you are using the default project structure (with the application, library, tests and public folders), you should use set up the db parameters in application/configs/application.ini
Example application.ini:
[production]
resources.db.adapter = "pdo_mysql"
resources.db.params.host = "localhost"
resources.db.params.username = "testuser"
resources.db.params.dbname = "testdb"
resources.db.params.password = "testpasswd"
resources.db.isDefaultTableAdapter = true
In this way zend framework will automatically open and close the connections to the database and you can use the Zend_Db_Table or Zend_Db_Table_Abstract classes to query your tables easily, for example, to retrieve the student's data for a given SSN you could write a model (application/models/Student.php) that looks something like this:
<?php
class Model_Student extends Zend_Db_Table_Abstract
{
protected $_name = "student";
public function fetchRowsBySSN($ssn)
{
$select = $this->select();
$select->where('ssn = ?', $ssn);
return $this->fetchRow($select)->toArray();
}
}
As you can see there's no need to open/close the connection and you get an associative array with the fields and values of the student record.
Your best best may be to move all of your db connection code into a separate class in which you can set a static $db var.
protected static $_db;
public static function connect()
{
if (self::$_db == null) {
$config = Zend_Config_Xml(); // whatever you'd use
self::$_db = Zend_Db::factory($config->database);
self::$_db->setFetchMode(Zend_Db::FETCH_OBJ);
self::$_db->query('SET NAMES UTF8');
Zend_Db_Table::setDefaultAdapter(self::$_db); // optional
}
return self::$_db;
}
public static function close()
{
if (self::$_db != null) {
self::$_db->closeConnection();
}
}
According to Zend:
Normally it is not necessary to close a database connection. PHP automatically cleans up all resources and the end of a request. Database extensions are designed to close the connection as the reference to the resource object is cleaned up.
However, if you have a long-duration PHP script that initiates many database connections, you might need to close the connection, to avoid exhausting the capacity of your RDBMS server. You can use the Adapter's closeConnection() method to explicitly close the underlying database connection.

Categories