3

For various reasons (security, among others) I have a Symfony2 application in which I want to specify credentials for multiple databases, but I do not want these stored anywhere on disk. I cannot store them as a system variable either and certainly don't want them to appear in Symfony Cache folder. I want to, at run time, be able to get my database credentials from another API which runs outside of Symfony (and is beyond the scope of the question, really). The code that does all of this needs to be in a Bundle, so we can use it across other apps too (and I've been asked to keep it in a bundle).

So, here is what I do in config.yml for a sample database connection (there can be more than one):

doctrine:
dbal:
    default_connection: default
    connections:
        default:
            driver:         %database_driver%
            dbname:         %database_name%
            charset:        UTF8
            server_version: 5.6

The driver and dbname are specified in parameters.yml.

In my main services.yml file, I have the following:

app.doctrine_default_connection_factory:
    class: Vendor\Bundle\MyBundle\CustomConnectionFactory\DoctrineFactory
    arguments: [@doctrine.dbal.default_connection, @logger]
    scope: request
    tags:
        - { name: kernel.event_listener, event: kernel.request, method: onKernelRequest }

The DoctrineFactory class does something along the following lines:

class DoctrineFactory  
{

    public function __construct(&$connection, $logger)
    {
        // Run some code to get credentials from elsewhere.
        // Update connection params.
        $params['user'] = $this->getUser();
        $params['password'] = $this->getPassword();
        $params['driverOptions'] = [
            \PDO::MYSQL_ATTR_SSL_CA => $this->getCaCert(),
            \PDO::MYSQL_ATTR_SSL_CERT => $this->getPublicCert(),
            \PDO::MYSQL_ATTR_SSL_KEY => $this->getPrivateKey()
        ];

        // If connected, close existing connection to the database.
        if ($connection->isConnected()) {
            $connection->close();
        }

        // Connect again with the new credentials.
        $connection->__construct(
            $params, $connection->getDriver(), $connection->getConfiguration(), $connection->getEventManager()
        );

        try {
            $connection->connect();
        } catch (Exception $e) {
            // log and handle exception
            $this->logger->error('Unable to connect to database: ' . $e->getMessage());
        }
    }

    public function onKernelRequest() {}
    // Other methods go here.

}

This code works fine pre PHP 5.5, but it starts throwing Exceptions from PHP 5.5+ because of the way Symfony parses the service requests and calls the DoctrineFactory class. In short, what happens is that when Symfony parses services.yml to write PHP (in app/cache/appXXXXContainer.php), it instantiates the DoctrineFactory class with the following code:

new Vendor\Bundle\MyBundle\CustomConnectionFactory\DoctrineFactory($this->get('doctrine.dbal.default_connection'), $this->get('logger'));

Which prompts PHP to throw an Exception because you cannot use a method to pass a variable by reference.

If, in the DoctrineFactory class, the $connection variable is not passed by reference, Symfony does not use the new credentials once that method finishes executing (I've tried returning the updated $connection variable), so for any code specified in other bundles or outside of this class that have database queries Doctrine does not have credentials. For this reason, I had to make $connection passed by reference.

Is there any way for me to overwrite the DB credentials specified in config.yml at runtime in a bundle without using this variable by reference?

There might be a better way to do this in Symfony that I'm not aware of, and I'm willing try anything as long as it works and isn't a non-maintainable hack of sorts.

Thank you.

Meezaan-ud-Din
  • 1,213
  • 16
  • 21
  • http://stackoverflow.com/questions/20805637/symfony2-dynamic-doctrine-database-connections-at-runtime http://stackoverflow.com/questions/15108732/symfony2-dynamic-db-connection-early-override-of-doctrine-service – Turdaliev Nursultan Nov 20 '15 at 12:04
  • I've already seen those, bu the solution similar to mine which I kind of used doesn't actually work without the &. – Meezaan-ud-Din Dec 03 '15 at 15:28
  • Have you tried this one? http://stackoverflow.com/a/28298990/3275814 It is by far the best. Notice he is overriding default doctrine service, you don't need to mess with already established connection. You are establising correct one with the first attempt. – lchachurski Jul 13 '16 at 19:12
  • Thank you, I'll see if this works as soon as I can add it back to my product backlog. – Meezaan-ud-Din Jul 20 '16 at 12:11

0 Answers0