We have moved our forum to GitHub Discussions. For questions about Phalcon v3/v4/v5 you can visit here and for Phalcon v6 here.

Apache crash on EventsManager::fire() provided with custom DB adapter instance to second parameter

I've implemented my own DB adapter extending \Phalcon\Db\Adapter. And it works fine untill I try to set an event manager instance for it. Regardless of what events are listened or even clear instance of \Phalcon\Events\Manager().

All the time I call EventsManager::fire() my apache crashes.

Here is an example:

<?php

namespace Phalcon\Db\Adapter;
use Phalcon\Db\Exception;

class Sybase extends \Phalcon\Db\Adapter implements \Phalcon\Db\AdapterInterface
{
    public function query($sqlStatement, $bindParams=null, $bindTypes=null)
    {
        /**
         * Execute the beforeQuery event if a EventsManager is available
         */
        if (is_object($this->_eventsManager)) {
            $this->_sqlStatement = $sqlStatement;
            $this->_sqlVariables = $bindParams;
            $this->_sqlBindTypes = $bindTypes;
            if ($this->_eventsManager->fire("db:beforeQuery", $this, $bindParams) == false) {
                return false;
            }
        }

        $sql = $this->getEmulatedBindQuery($sqlStatement, $bindParams, $bindTypes);
        $statement = sybase_query($sql, $this->_connection);

        if ($statement === false) {
            throw new Exception('Query "' . $sql . '" caused following error: "' . sybase_get_last_message() . '"');
        } else {
            /**
             * Execute the afterQuery event if a EventsManager is available
             */
            if (is_object($this->_eventsManager)) {
                $this->_eventsManager->fire("db:afterQuery", $this, $bindParams);
            }

            return new \Phalcon\Db\Result\Sybase($this, $statement, $sql);
        }
    }

Versions: PHP 5.5.10, Apache 2.4.9.0, Phalcon 1.3.0

I would be grateful for any help.



8.1k

Could you enable PHP syntax highlighting? Makes it a bit more friendly on the eyes...



8.1k

I don't have a clue actually, not really familiar in this area. But I guess you already have had a look here: https://github.com/phalcon/cphalcon/blob/2.0.0/phalcon/db/adapter/pdo.zep ?

How does the evManager instantation / assignment look like?

Does Apache log an error when it crashes?



902

Yes I used ZEP sources of phalcon 2.0 as a start point before implementing my own class.

Event manager instantiation and assignment looks like this:

$di->set('db', function () use ($config, $di) {

    $eventsManager = new \Phalcon\Events\Manager();

    //Get a shared instance of the DbProfiler
    $profiler = $di->getProfiler();

    //Listen all the database events
   $eventsManager->attach('db', function($event, $connection) use ($profiler) {
        if ($event->getType() == 'beforeQuery') {
            $profiler->startProfile($connection->getSQLStatement());
        }
        if ($event->getType() == 'afterQuery') {
            $profiler->stopProfile();
        }
    });
    $connection = new DbAdapter(array(
        "servername" => $config->database->servername,
        "username" => $config->database->username,
        "password" => $config->database->password,
        "dbname" => $config->database->dbname
    ));

    //Assign the eventsManager to the db adapter instance
    $connection->setEventsManager($eventsManager);

    return $connection;
}, true);

But it might look like this, result is still the same:

$di->set('db', function () use ($config, $di) {

    $eventsManager = new \Phalcon\Events\Manager();

    $connection = new DbAdapter(array(
        "servername" => $config->database->servername,
        "username" => $config->database->username,
        "password" => $config->database->password,
        "dbname" => $config->database->dbname
    ));

    //Assign the eventsManager to the db adapter instance
    $connection->setEventsManager($eventsManager);

    return $connection;
}, true);

Apache doesn't log anything, it's process just halts.

Finally at this moment I've discovered one valuable fact: if I pass my object casting it to stdClass the apache doesn't crash. What I change:


        if ($this->_eventsManager->fire("db:beforeQuery", (object)(array)$this, $bindParams) == false) {

Of course it's not a solution, but it is no more doubts that it's my adapter "responsible" for this crash. Unfortunatelly It's still not clear what is missing in my implementation. I'll review interface an native implementations in ZEP and try to find out what is missing. And post here later if there will be any progress.



902
edited Jun '14

And here is result of my investigation so far. The problem is not in DB adapter implementation. It's on a higher level. Apache crashes every time Adapter::query() returns false. As my listeners did not return anything, as well as empty EventsManager without listeners, this part

            if ($this->_eventsManager->fire("db:beforeQuery", $this, $bindParams) == false) {
                return false;
            }

always returns false. So the simple workaround is make your listeners always return true at least for "beforeQuery" event. But what about the case you want to implement some complex logic based on events and some cases require query execution cancelling?

And the last, most weird thing: When I try the same trick with mysql adapter ORM behaves completely different. If listeners doesn't return anything query is executed. If listeners return false explicitly query execution stops and apache is alive. Let's dig into the source:

pdo.c PHP_METHOD(Phalcon_Db_Adapter_Pdo, query)

        PHALCON_CALL_METHOD(&status, events_manager, "fire", event_name, this_ptr, bind_params);
        if (PHALCON_IS_FALSE(status)) {
            RETURN_MM_FALSE;
        }

Initially status is null and passed by reference, if it doesn't change in the listener it is still null, PHALCON_IS_FALSE(status) may not be true and method goes on. But what about secont case? When status becomes false method query exits with this statement: RETURN_MM_FALSE; and apache stay alive. Does it mean this statement is not the same as returning false in php analogical method?