PDO returned as Object: Breaks Object Self State Management

Hi,

This Auth Class has an ugly line (in the __construct()) that I have been trying to find a way to rework:

<?php
class Auth{
    protected $db;
    
    function __construct(DbFactory $db_factory){
        $this->db = $db_factory->getDb();
    }
    /*test code*/
    function getResults(){
        $sql ="SELECT * FROM users";
        
        $sth = $this->db->prepare($sql);
        $sth->execute();
        $columns = $sth->fetchAll();
        foreach ($columns as $column) {
            foreach ($column as $field){
                echo $field . '<br />';
            }
        }
    }
}
?>

The class called to create the PDO instance is:

<?php 
class DbFactory {
    protected $db;
    function __construct() {
        $this->db= new PDO("mysql:host=localhost;dbname=users", "user", "secret");
    }
    
    function getDb() {
        return $this->db;
    }
}
?>

When running this bootstrap file this runs successfully:

<?php
require_once("bucket.inc.php");
function __autoload($class_name) {
    require_once $class_name . '.php';
}

$auth_container = new bucket_Container();
$auth = $auth_container->get("Auth");
/*test code*/
echo $auth->getResults();
?>

I have tried changing class DbFactory to:

<?php 
class DbFactory {
    function __construct() {
        return new PDO("mysql:host=localhost;dbname=users", "user", "secret");
    }
    
}
?>

and

<?php 
class DbFactory {
    protected $db;
    function __construct() {
        $this->db= new PDO("mysql:host=localhost;dbname=users", "user", "secret");
        return $this->getDb();
    }
    
    function getDb() {
        return $this->db;
    }
}
?>

but in both cases it returns the DbFactory object rather than the actual PDO object - for example:

object(DbFactory)#10 (1) {   ["db:protected"]=>   object(PDO)#11 (0) {   } }

When using the smelly code way it correctly returns

object(PDO)#25 (0) { } 

Rather than having the Auth Class be aware of the getDb() method in the DbFactory Class, in using the way that the DbFactory class is instantiated (with the DI Bucket) that it can return just the PDO object?

Regards,
Steve

I don’t see the need for the DbFactory class.

The Auth class has a dependency on the PDO interface (with Auth::getResults() using PDO::prepare() and so on) so why not explicitly state it in the constructor?

This is would be my personal way of doing something like this…


interface Auth
{
	function getResults();
}

class PDOAuth implements Auth
{
    protected $pdo;

    function __construct(PDO $pdo)
    {
        $this->pdo = $pdo;
    }

    function getResults()
    {
        $sql ="SELECT * FROM users";
        $sth = $this->pdo->prepare($sql);
        $sth->execute();
        $columns = $sth->fetchAll();
        foreach ($columns as $column) {
            foreach ($column as $field){
                echo $field . '<br />';
            }
        }
    }
}

// Wiring...
	$container = new bucket_Container(
		array(
			'PDO' => function() { return new PDO('sqlite::memory:', '', ''); }
		)
	);
	$container->registerImplementation('Auth', 'PDOAuth');
	
// Application code...
	$auth = $container->get('Auth');
	var_dump($auth);

PS: think some strlower() oddness in around line 86-87 of Bucket.


if (isset($this->factory->{'new_' . strtolower($classname)})) {
      return call_user_func($this->factory->{'new_'.$classname}, $this);
    }

Doesn’t make sense to me.

Hi Ren,

Thanks for your examples!

In your scenario would you not have to create an PDO interface and implement it for every Class that had a Db dependency?

Although not shown, the Auth class has many more dependencies such as Session, Hash (has Db dependency), and log-in tracker. With this case would it not complicate supporting these dependencies in a loose coupled way?

Regards,
Steve

Yes. Eg


interface UserMapper
{
    function save(User $user)
}

class PDOUserMapper implements UserMapper
{
    protected $pdo;
    function __construct(PDO $pdo) { $this->pdo = $pdo; }
    function save(User $user) { .... }
}

Although not shown, the Auth class has many more dependencies such as Session, Hash (has Db dependency), and log-in tracker. With this case would it not complicate supporting these dependencies in a loose coupled way?

Regards,
Steve[/QUOTE]

Shouldn’t have any problems.

Have an interface for Session, and Hash, and then implementations of both.
The container can be wired so create concrete implementations from the interface requirements.

Ok Ren,

Again thanks. However when I use this code:

<?php
require_once("bucket.inc.php");
function __autoload($class_name) {
    require_once $class_name . '.php';
}

// Wiring...
    $container = new bucket_Container(
        array(
            'PDO' => function() { 
return new PDO("mysql:host=localhost;dbname=users", "user", "secret"); }
        )
    );
    $container->registerImplementation('Auth', 'PDOAuth');
    
// Application code...
    $auth = $container->get('Auth');
    var_dump($auth); 

/*test code*/
echo $auth->getResults();
?>

I get this error:


[B]Parse error[/B]:  syntax error, unexpected T_FUNCTION in [B]
/path/to/auth_bootstrap.php[/B] 
on line [B]10[/B]

which is the line

array(
            'PDO' => function() { return new 
PDO("mysql:host=localhost;dbname=users", "user", "secret"); }
        )

Is this PHP 5.3 specific, because we are using 5.2.4. or might this be a php configuration issue?

Regards,
Steve

Yeah it is 5.3 specific.

I think you could do something like


class MyFactory
{
  function new_PDO()
  {
    return new PDO("mysql:host=localhost;dbname=users", "user", "secret");
  }
}

    $container = new bucket_Container(new MyFactory());

Ok, too bad as it puts it back to the smelly code again :rolleyes:

Regards,
Steve

Well using Ren’s code I got this a little less smelly. Here it is:

PDOAuth Class:

<?php
interface Auth
{
    function getResults();
}

class PDOAuth implements Auth
{
    protected $pdo;

    function __construct(DbFactory $pdo)
    {
        $this->pdo = $pdo;
    }

    function getResults()
    {
        $sql ="SELECT * FROM users";
        $sth = $this->pdo->db->prepare($sql);
        $sth->execute();
        $columns = $sth->fetchAll();
        foreach ($columns as $column) {
            foreach ($column as $field){
                echo $field . '<br />';
            }
        }
    }
}
?>

notice access to the pdo object in the line under getResults is

 $sth = $this->pdo->db->prepare($sql);

The

$this->pdo->db
would be confusing to anyone else using this code, but because I could not return this property from the DbFactory, I have to reference it like that.

Here is the DbFactory Class (not much of a class at all :goof: mostly like a mutable value object):

<?php 
class DbFactory {
    public $db;
    
    function __construct() {
      $this->db = new PDO("mysql:host=localhost;dbname=users", "user", "secret");    
    }
}

?>

The Bootstrap files:

<?php
require_once("bucket.inc.php");
function __autoload($class_name) {
    require_once $class_name . '.php';
}

// Wiring...
   $container = new bucket_Container(new DbFactory);
// Application code...
   $container->registerImplementation('Auth', 'PDOAuth');
    
// Application code...
   $auth = $container->get('Auth');

echo $auth->getResults();
?>

Big THANKS REN - now the code is a little improved;

Regards,
Steve

How do you mean? That’s how I would recommend that you do it.

Hi kyberfabrikken

Does this line not mean that now the PDOAuth class needs to be aware of the DbFactory $db property, thus still breaking the encapsulation and self discovering dependencies? If I am on the right track, that is what I meant.

 $sth = $this->pdo->db->prepare($sql);  

Thanks,
Steve

No it wouldn’t - only the container knows about the factory. PDOAuth just depends on PDO.

kyberfabrikken ‘Jackalope’ :slight_smile: Thank you!