Blog Archives

Strange behavior in PHP with method visibility

Normally I feel very comfortable with PHP, but not all is good. There’s some things I don’t like. One is the lack of real annotations and another one is this rare behaviour with visibility within the OO. Let me explain this a little bit.

Last week I was refactoring one old script. I removed a coupling problem with DI. Something like this:

class AnotherClass
{
    protected function foo()
    {
        return "bar";
    }
}

class OneClass extends AnotherClass{
    private $object;

    public function __construct(AnotherClass $object)
    {
        $this->object = $object;
    }

    public function myFunction()
    {
        return $this->object->foo();
    }
}

$anotherClass = new AnotherClass();
$oneClass = new OneClass($anotherClass);

echo $oneClass->myFunction();

It works, but I realized that I didn’t need to extend OneClass with AnotherClass (due to the DI), so I removed it. Then the script crashed:
Fatal error: Call to protected method AnotherClass::foo() from context ‘OneClass’

Obviously it was due to the protected function AnotherClass::foo. But, Why it worked when I extends OneClass with AnotherClass? The visibility is the same.

I reported this “bug” to the PHP community. PHP community is great. I had an answer very quick. It was not a bug. I needed to read several times the answer to understand it but finally did it.

As someone answer me:

foo() is protected and was defined in the context of OneClass. The access is done in the context of AnotherClass. AnotherClass is a subclass of OneClass (the context where foo() was defined). Therefore access is granted

In PHP the visibility belongs to the class not to the instance of the class. I understand the reason, but my mind compute it as a bug :( and it isn’t. It’s a feature.

What do you think?

Database connection pooling with PHP and React (node.php)

Last saturday I meet a new hype: “React” also known as “node.php”. Basically it’s the same idea than node.js but built with PHP instead of javascript. Twitter was on fire with this new library (at least my timeline). The next sunday was a rainy day and because of that I spent the afternoon hacking a little bit with this new library. Basically I want to create a database connection pooling. It’s one of the things that I miss in PHP. I wrote a post here some time ago with this idea with one exotic experiment building one connection pooling using gearman. Today the idea is the same but now with React. Let’s start

First of all we install React. It’s a simple process using composer.

% echo '{ "require": { "react/react": "dev-master" } }' > composer.json
% composer install

Now we can start with our experiment. Imagine a simple query to PostgreSql using PDO:

CREATE TABLE users
(
  uid integer NOT NULL,
  name character varying(50),
  surname character varying(50),
  CONSTRAINT pk_users PRIMARY KEY (uid)
)
WITH (
  OIDS=FALSE
);
ALTER TABLE users OWNER TO gonzalo;

INSERT INTO users(uid, name, surname) VALUES (0, 'Gonzalo', 'Ayuso');
INSERT INTO users(uid, name, surname) VALUES (1, 'Hans', 'Solo');
INSERT INTO users(uid, name, surname) VALUES (2, 'Luke', 'Skywalker');
$dbh = new PDO('pgsql:dbname=demo;host=vmserver', 'gonzalo', 'password');
$sql = "SELECT * FROM USERS";
$stmt = $dbh->prepare($sql);
$stmt->execute();
$data = $stmt->fetchAll();
print_r($data);

Now we are going to use the same interface but instead of using PDO we will use one server with React:

include "CPool.php";
define('NODEPHP', '127.0.0.1:1337');

$dbh = new CPool();
$sql = "SELECT * FROM USERS";
$stmt = $dbh->prepare($sql);
$stmt->execute();
$data = $stmt->fetchAll();
$stmt->closeCursor();
print_r($data);

Our CPool library:

class CPoolStatement
{
    private $stmt;
    function __construct($sql=null)
    {
        if (!is_null($sql)) {
            $url = "http://" . NODEPHP . "?" . http_build_query(array(
                    'action' => 'prepare',
                    'sql'    => $sql
                 ));
            $this->stmt = file_get_contents($url);
        }
    }

    public function getId()
    {
        return $this->stmt;
    }

    public function setId($id)
    {
        $this->stmt = $id;
    }

    public function execute($values=array())
    {
        $url = "http://" . NODEPHP . "?" . http_build_query(array(
                'action' => 'execute',
                'smtId'  => $this->stmt,
                'values' => $values
             ));
        $this->stmt = file_get_contents($url);
    }

    public function fetchAll()
    {
        $url = "http://" . NODEPHP . "?" . http_build_query(array(
                'action' => 'fetchAll',
                'smtId'  => $this->stmt
             ));
        return (file_get_contents($url));
    }

    public function closeCursor()
    {
        $url = "http://" . NODEPHP . "?" . http_build_query(array(
                'action' => 'closeCursor',
                'smtId'  => $this->stmt
             ));
        return (file_get_contents($url));
    }
}

class CPool
{
    function prepare($sql)
    {
        return new CPoolStatement($sql);
    }
}

We also can create one script that creates one statement

include "CPool.php";
define('NODEPHP', '127.0.0.1:1337');

$dbh = new CPool();
$sql = "SELECT * FROM USERS";
$stmt = $dbh->prepare($sql);

echo $stmt->getId();

And another script (another http request for example) to fetch the resultset. Notice that we can execute this script all the times that we want because the compiled statement persists in the node.php server (we don’t need to create it again and again within each request).

include "CPool.php";
define('NODEPHP', '127.0.0.1:1337');

$stmt = new CPoolStatement();
$stmt->setId(1);

$stmt->execute();
$data = $stmt->fetchAll();
print_r($data);

And basically that was my sunday afternoon experiment. As you can imagine the library is totally unstable. It’s only one experiment. We can add transaccions, comits, rollbacks, savepoints, … but I needed a walk and I stopped:). What do you think?

The code is available at github

Inject dependencies via PhpDoc

Last month I attended to Codemotion conference. I was listening to a talk about Java and I saw the “@inject” decorator. I must admit I switched off my mind from the conference and I started to take notes in my notebook. The idea is to implement something similar in PHP. It’s a pity we don’t have real decorators in PHP. I really miss them. We need to use PhpDoc. It’s not the same than real decorators in other programming languages. That’s my prototype. Let’s go.

Imagine this simple class:

class User
{
    private $db;

    public function getInfo($uid)
    {
        $sql = "select * from users where uid=:UID";
        $stmp = $this->db->prepare($sql);
        $stmp->execute(array('UID' => $uid));
        return $stmp->fetchAll();
    }

    public function getDb()
    {
        return $this->db;
    }
}

It doesn’t work. Private property $db must be an instance of PDO object. We can solve it with dependency injection:

class User
{
    private $db;
    public function __construct(PDO $db)
    {
        $this->db = $db;
    }

    public function getInfo($uid)
    {
        $sql = "select * from users where uid=:UID";
        $stmp = $this->db->prepare($sql);
        $stmp->execute(array('UID' => $uid));
        return $stmp->fetchAll();
    }
}

Now it works but we are going to create a simple PDO Wrapper to obtain the PDO connection.

class DbConf
{
    const DB1 = 'db1';

    private static $conf = array(
        self::DB1 => array(
            'dsn'  => 'pgsql:dbname=db;host=localhost',
            'user' => 'gonzalo',
            'pass' => 'password',
        )
    );

    public static function getConf($key)
    {
        return self::$conf[$key];
    }
}

class Db extends PDO
{
    private static $dbInstances = array();

    /**
     * @static
     * @param string $key
     * @return PDO
     */
    static function getDb($key)
    {
        if (!isset($dbInstances[$key])) {
            $dbConf = DbConf::getConf($key);
            $dbInstances[$key] = new PDO($dbConf['dsn'], $dbConf['user'], $dbConf['pass']);
        }
        return $dbInstances[$key];
    }
}

I like to use this kind of classes because I normally work with different databases and I need to use different connection depending on the SQL. It helps me to mock the database connection within the different environments (development, production, QA). Now We can use our simple class:

$user = new User(Db::getDb(DbConf::DB1));
print_r($user->getInfo(4));

The idea is to change the class into something like this:

class User extends DocInject
{
    /**
     * @inject Db::getDb(DbConf::DB1)
     * @var PDO
     */
    private $db;

    public function getInfo($uid)
    {
        $sql = "select * from users where uid=:UID";
        $stmp = $this->db->prepare($sql);
        $stmp->execute(array('UID' => $uid));
        return $stmp->fetchAll();
    }
}

Now we are going to inject the PDO connection to $db private property in the constructor:

class DocInject
{
    public function __construct()
    {
        $reflection = new ReflectionClass($this);
        foreach ($reflection->getProperties() as $property) {
            /** @var ReflectionProperty $property */
            $docComment = $property->getDocComment();
            $docComment = preg_replace('#[ \t]*(?:\/\*\*|\*\/|\*)?[ ]{0,1}(.*)?#', '$1', $docComment);
            $docComment = trim(str_replace('*/', null, $docComment));
            foreach (explode("\n", $docComment) as $item) {
                if (strpos($item, '@inject') !== false) {
                    $inject = trim(str_replace('@inject', null, $item));
                    $value = null;
                    eval("\$value = {$inject};"); // yes, eval. uggly, isnt't?
                    $property->setAccessible(true);
                    $property->setValue($this, $value);
                }
            }
        }
    }
}

If you have read “Clean Code” (if not, you must do it) you noticed that uncle Bob doesn’t like this class. The method is too long, so we are going to refactor a little bit.

class DocInject
{
    public function __construct()
    {
        $reflection = new ReflectionClass($this);
        foreach ($reflection->getProperties() as $property) {
            $this->processProperty($property);
        }
    }

    private function processProperty(ReflectionProperty $property)
    {
        $docComment = $this->cleanPhpDoc($property->getDocComment());
        foreach (explode("\n", $docComment) as $item) {
            if ($this->existsInjectDecorator($item)) {
                $this->performDependencyInjection($property, $item);
            }
        }
    }

    private function cleanPhpDoc($docComment)
    {
        $docComment = preg_replace('#[ \t]*(?:\/\*\*|\*\/|\*)?[ ]{0,1}(.*)?#', '$1', $docComment);
        $docComment = trim(str_replace('*/', null, $docComment));
        return $docComment;
    }

    private function existsInjectDecorator($item)
    {
        return strpos($item, '@inject') !== false;
    }

    private function performDependencyInjection(ReflectionProperty $property, $item)
    {
        $injectString = $this->removeDecoratorFromPhpDoc($item);
        $value = $this->compileInjectString($injectString);
        $this->injectValueIntoProperty($property, $value);
    }

    private function removeDecoratorFromPhpDoc($item)
    {
        return trim(str_replace('@inject', null, $item));
    }

    private function compileInjectString($injectString)
    {
        $value = null;
        eval("\$value = {$injectString};"); // yes, eval. uggly, isnt't?
        return $value;
    }

    private function injectValueIntoProperty(ReflectionProperty $property, $value)
    {
        $property->setAccessible(true);
        $property->setValue($this, $value);
    }
}

So now we don’t need to pass the new instance of PDO connection in the constructor with DI:

$user = new User();
print_r($user->getInfo(4));

It works but there’s something that I don’t like. We need to extend our User class with DocInject. I like plain classes. Because of that we are going to use the new feature of PHP5.4: traits

Instead of extend our class with DocInject we are going to create:

trait DocInject
{
    public function parseDocInject()
    {
        $reflection = new ReflectionClass($this);
        foreach ($reflection->getProperties() as $property) {
            $this->processProperty($property);
        }
    }

    private function processProperty(ReflectionProperty $property)
    {
        $docComment = $this->cleanPhpDoc($property->getDocComment());
        foreach (explode("\n", $docComment) as $item) {
            if ($this->existsInjectDecorator($item)) {
                $this->performDependencyInjection($property, $item);
            }
        }
    }

    private function cleanPhpDoc($docComment)
    {
        $docComment = preg_replace('#[ \t]*(?:\/\*\*|\*\/|\*)?[ ]{0,1}(.*)?#', '$1', $docComment);
        $docComment = trim(str_replace('*/', null, $docComment));
        return $docComment;
    }

    private function existsInjectDecorator($item)
    {
        return strpos($item, '@inject') !== false;
    }

    private function performDependencyInjection(ReflectionProperty $property, $item)
    {
        $injectString = $this->removeDecoratorFromPhpDoc($item);
        $value = $this->compileInjectString($injectString);
        $this->injectValueIntoProperty($property, $value);
    }

    private function removeDecoratorFromPhpDoc($item)
    {
        return trim(str_replace('@inject', null, $item));
    }

    private function compileInjectString($injectString)
    {
        $value = null;
        eval("\$value = {$injectString};"); // yes, eval. uggly, isnt't?
        return $value;
    }

    private function injectValueIntoProperty(ReflectionProperty $property, $value)
    {
        $property->setAccessible(true);
        $property->setValue($this, $value);
    }
}

And now:

class User
{
    use DocInject;

    public function __construct()
    {
        $this->parseDocInject();
    }

    /**
     * @inject Db::getDb(DbConf::DB1)
     * @var PDO
     */
    private $db;

    public function getInfo($uid)
    {
        $sql = "select * from users where uid=:UID";
        $stmp = $this->db->prepare($sql);
        $stmp->execute(array('UID' => $uid));
        return $stmp->fetchAll();
    }
}

This implementation has a little problem. If our class User needs a constructor we have a problem. As far as I know we cannot use parent::__construct() with a trait. We can solve this problem changing the code a little bit:

class User
{
    use DocInject {parseDocInject as __construct;}

    /**
     * @inject Db::getDb(DbConf::DB1)
     * @var PDO
     */
    private $db;

    public function getInfo($uid)
    {
        $sql = "select * from users where uid=:UID";
        $stmp = $this->db->prepare($sql);
        $stmp->execute(array('UID' => $uid));
        return $stmp->fetchAll();
    }
}

A simple unit test

    public function testSimple()
    {
        $user = new User();
        $this->assertTrue(count($user->getInfo(4)) > 0);
    }

If we use different DbConf file for each environment we can easily use one database or another without changing any line of code.

And that’s all. What do you think?

(Files available as gist here and here)

Follow

Get every new post delivered to your Inbox.

Join 869 other followers