Freitag, 13. Juni 2014

Parse PS Output in PHP

Lange ist mein letzter Post her, hier nun mal wieder ein kleines Script von mir. Ich musste anhand des "Command-String" testen ob ein Process rennt. Ich habe mich hierfür der Ausgabe von "ps aux" bedient. Diese Ausgabe in PHP zu parsen, war nach einer kurzen Google-Recherche angeblich nicht als zu trivial. Nun habe ich mich mal komplett selbst dran gesetzt und bin zu folgendem Ergebnis gekommen:

    const PS_COLUMNS_COUNT = 11;

    protected function parse($psOutput) {
        $rows = array();
        $lines = explode("\n", $psOutput);
        $headerColums = preg_split('![\s]+!', array_shift($lines));
        foreach ($lines AS $line) {
            $lineColumns = preg_split('![\s]+!', $line, self::PS_COLUMNS_COUNT);
            if (count($lineColumns) == self::PS_COLUMNS_COUNT) {
                $rows[] = array_combine($headerColums, $lineColumns);
            }
        }
        return $rows;
    }

im Grunde recht simple. So long...

Mittwoch, 10. Juli 2013

vTiger ist ja soooo toll!

Jetzt ist es wieder so weit, ich muss meinen Frust irgend jemanden erzählen! Die aktuelle Aufgabenstellung war ein CRM mit Asterisk anbindung auf zu setzen. Ein kurze Rücksprache mit Google ergab, dass es da diverse Lösung für gibt. Kostenpflichtige sind gleich mal alle ausgeschieden, weil eben kostenpflichtig ;) Nun gab eben noch die Klassiger, nach einem verzweifelten Versuch die Asterisk-Integration unter "sugarCRM" zum laufenzu bekommen bin ich dann doch zu "vTiger 5.4.0" umgestiegen, da hier ja angeblich schon alles "Out-Of-The-Box" dabei sein soll. Gutgläubig wie ich nun habe ich mich den Abenteuer gestellt und diesmal sollte es ein Erfolg werden! Leider habe ich da den Indern wohl zu viel zugemutet, wer es nicht weiß, vTiger ist ein Ableger von sugarCRM und wurde von einer indischen Firma entwickelt.
Die installation auf einem Ubuntu-Server über den binary-installer ging mal volle kanne nach hinten los, versucht es erst gar nicht, das klappt eh nicht! Also bin ich zur "Source"-Variante umgestiegen. Nachdem ich von PHP 5.4 ein Downgrade auf 5.3 gemacht habe, lief sogar der Web-Installer von vTiger. Alles sah echt gut aus und ich war echt guter Dinge jetzt einfach durchstarten zu können! Weit gefehlt, die nächste Hürde hat nicht lange auf sich warten lassen.
Die Asterisk-Integration ist wohl so gut wie unmöglich zu verallgemeinern. Im PBX-Modul habe ich schnell mal den Zugang zur Asterisk AMI eingerichtet und dachte nun wird alles gut, weit gefehlt! Ich habe ein Asterisk 1.8 im Einsatz und von vTiger wird derzeit nur Asterisk 1.6 unterstützt und das habe ich auch gemerkt. Ausgehende Anrufe gingen eigentlich recht problemlos, nur die Eingehenden, die haben sich nicht blicken lassen. Kein schönes Popup welches mir anzeigt wer da gerade schönes anruft. Die Leidensgenosen bei Google berichteten von einem "AsteriskClient.php" und dass dort irgendwas angepasst werden soll. Ja, irgendwas... Ich habe mir den Code angesehen und auch recht schnell verstanden was die da vorhaben. Darauf hin habe ich mir den Output von meinem Asterisk angesehen:

# php /var/www/vtigercrm/cron/modules/PBXManager/AsteriskClient.php
Schnell wurde mir klar, das was mein Asterisk da ausspuckt und das was der "AsteriskClient" erwartet passt nicht zusammen. Der Dreh und  Angelpunkt ist die Function "asterisk_handleResponse2", hier wird erkannt für welchen SIP-Account ein Anruf gerade rein kommt. Nachdem ich folgende Änderung vorgenommen habe und es so an meine Bedürfnisse angepasst habe wurden die Eingehenden Anrufe schonmal erkannt:

function asterisk_handleResponse2($mainresponse, $adb, $asterisk, $state) {
    //AppData: SIP/1000&SIP/2000,20,TtrM(auto-blkvm)
    $appdata = $mainresponse['AppData'];

    $uniqueid = $channel = $callerType = $extension = null;
    $parseSuccess = false;

    if (
//        $mainresponse['Event'] == 'Newexten' && (strstr($appdata, "__DIALED_NUMBER") || strstr($appdata, "EXTTOCALL"))
            $mainresponse['Event'] == 'Newexten' && (preg_match('!^(SIP/[0-9]+&?)+,!', $appdata) )
    ) {

        $uniqueid = $mainresponse['Uniqueid'];

        $channel = $mainresponse['Channel'];
        $splits = explode('/', $channel);
        $callerType = $splits[0];

        $appDataSplits = explode(',', $appdata);
        $sipSplits = explode('&', array_shift($appDataSplits));
        $extSplits = explode('/', array_shift($sipSplits));
        $extension = $extSplits[1];

        if (count($sipSplits) > 0) {
            $mainresponse['AppData'] = implode('&', $sipSplits) . ',' . implode(',', $appDataSplits);
            asterisk_handleResponse2($mainresponse, $adb, $asterisk, $state);
        }


        $parseSuccess = true;
    } else if ($mainresponse['Event'] == 'OriginateResponse') {
        //if the event is OriginateResponse then its an outgoing call and set the flag to 1, so that AsteriskClient does not pick up as incoming call
        $uniqueid = $mainresponse['Uniqueid'];
        $adb->pquery("UPDATE vtiger_asteriskincomingevents set flag = 1 WHERE uid = ?", array($uniqueid));
    }

....
 Zu sehen ist auch, dass in meiner Asterisk-Konfiguration für ein Nummer mehrer SIP-Client angesteuert werden, so habe ich für jeden dieser gemeldeten SIP-Client die Function entsprechend erneut aufgerufen. Nun dachte ich, alles wird erkannt und landet in der Datenbank, nun ist das Problem auch beseitigt. Leider ging es weiter... Nun ist zwar dieses schöne Popup aufgetaucht aber es zeigte mir jeden Anrufer so an, als wäre diese Nummer nicht als Firma oder Kunde in der Datenbank hinterlegt? Ich konnte mir dies gar nicht erklären, die Nummer wurde doch nun korrekt von Asterisk erkannt und auch in die DB geschrieben, doch da habe ich es gesehen. Im Popup fehlte die führende Null! Wie ist nun diese Null abhanden gekommen? Das Rätsels Lösung wasdie Datenbank, da laut vTiger für das entsprechende Feld ein "BigINT" mit 20 Stellen vorgesehen war. Und was passiert mit führenden Nullen bei einem INT? Richtig, die verschwinden einfach! Also habe ich das entsprechende Feld in der Datenbank angepasst:

ALTER TABLE `vtiger_asteriskincomingevents` CHANGE COLUMN `from_number` `from_number` VARCHAR(50) NULL DEFAULT NULL  ;
Nun wurde die Nummer in der Datenbank korrekt gespeichert. Zu erwähnen ist aber, dass bei einem eingehenden Anruf, diese Nummer 1zu1 verglichen wird um den entsprechenden Kunden zu finden. Somit sollte man vermeiden Vorwahl und Rufnummer durch irgend ein Zeichen zu trenne, sondern einfach am Stück zu hinterlegen, sonst findet das Popup-Modul den Kunden nicht! Ein weiteres Mysterium wurde bezwungen und läuft nun.
Nun sollte man von ausgehen, dass der Rest problemlos läuft? Leider nicht, weiter ging es bei mir mit der Einrichtung vom E-Mail-Server. Die systemweite SMTP-Server Einstellung befindet sich in den Einstellungen und erwartet entsprechende Zugangsdaten. Schnell habe ich extra dafür ein neues E-Mail-Konto eingerichtet und alle Daten hinterlegt. Beim Speichern werden diese Einstellungen überprüft. Ich konnte machen was ich wollte, vTiger hat meine Settings nicht gefressen, immer nur mit einer nichtssagenden Fehlermeldung, dass keine Test-Mail gesendet werden konnte. Nach etlichen Zeilen Code welche ich wieder gelesen habe und hardcodierte Debug Ausgabeschalter konnte ich nun herrausfinden, dass vTiger grundsätzlich davon ausgeht, dass der verwendete SMTP-Server mit der Domain von der E-Mail Adresse übereinstimmt. Mein verwendeter SMTP-Server ist bei Hosteurope und wird als Host über das Web-Paket angesprochen:

wpXXX.webpack.hosteurope.de
Nun, was tut vTiger an dieser stelle. Es nimmt als "Sender-E-Adresse" genau diese Domain, schnibbelt noch vorne den ersten Teil ab, also hier "wpXXX" und baut sich daraus dann eine E-Mail-Adresse: noreply@webpack.hosteurope.de. Diese E-Mail-Andresse hat mein SMTP-Server nicht zugelassen und entsprechend den Versand verweigert! Darauf hin musste ich mal wieder den Source etwas anpassen. In der Datei "/var/www/vitigercrm/modules/Emails/mail.php" in der Function "setMailerProperties" so um die Zeile 195 gibt es folgenden Code:

$mail->Sender= getReturnPath($mail->Host);
Diese Zeile führt zu oben genanntes Verhalten. Darauf hin habe ich diese Zeile geringfügig angepasst:

$mail->Sender = $from_email ? $from_email : getReturnPath($mail->Host);
Und schon läuft auch der eingetragene SMTP-Server mit einer anderen Domain. Ich habe an dieser Stelle nun erstmal ein "Break" gemacht und nicht weiter versucht die Features von vTiger zu nutzen. Ich denke da wird es noch die eine oder andere Situation geben wo Nachgebessert werden muss.
Vielleicht konnte ich mit meinen Erfahrungen an vTiger dem einen oder anderen etwas Helfen.

Bis die Tage,
  greez, Volker...

Dienstag, 18. Juni 2013

IP-Berechnung in PHP

Nun ist es wieder so weit, ich habe mir in PHP ein kleine Klasse gebastelt mit welcher ich alles Rund um IPv4 berechnen kann. Natürlich gibt es da noch das eine oder andere was man hinzufügen könnte aber mir reichen diese Functions:

Zuerst einmal ein Entity um die IP-Definition zu speichern:

class NetworkAddress {

    /**
     * @var string
     */
    private $ip;

    /**
     * @var integer
     */
    private $asInteger;

    function __construct($ip = null, $asInteger = null) {
        $this->setIp($ip);
        $this->setAsInteger($asInteger);
    }

    /**
     * Get id
     * @return integer
     */
    public function getId() {
        return $this->id;
    }

    /**
     * Set ip
     * @param string $ip
     * @return NetworkAddress
     */
    public function setIp($ip) {
        $this->ip = $ip;
        return $this;
    }

    /**
     * Get ip
     * @return string
     */
    public function getIp() {
        return $this->ip;
    }

    /**
     * Set asInteger
     * @param integer $asInteger
     * @return NetworkAddress
     */
    public function setAsInteger($asInteger) {
        $this->asInteger = floor((float) $asInteger);
        return $this;
    }

    /**
     * Get asInteger
     * @return integer
     */
    public function getAsInteger() {
        return (floor) $this->asInteger;
    }

}
Und hier die eigentlich Logik:
class Network {

    const MAX_IP_AS_INT = 4294967295; //255.255.255.255

    /**
     * korrigiert die inegabe von CIDR
     * @param int $cidr
     * @return int
     */

    static protected function fixCIDR($cidr) {
        return max(0, min(32, (int) $cidr)); //cidr zwischen 0 - 32
    }

    /**
     * @param integer $asInt
     * @return \NetworkAddress
     * @throws \Excpetion
     */
    static public function generateNetworkAddressByInteger($asInt) {
        $asInt = floor((float) $asInt);
        if ($asInt >= 0 && $asInt <= self::MAX_IP_AS_INT) {
            return new NetworkAddress(long2ip($asInt), $asInt);
        } else {
            throw new \Exception('Invalid integer range: ' . $asInt);
        }
    }

    /**
     * @param string $asString
     * @return \NetworkAddress
     * @throws \Excpetion
     */
    static public function generateNetworkAddressByString($asString) {
        if (filter_var($asString, FILTER_VALIDATE_IP)) {
            return new NetworkAddress($asString, ip2long($asString));
        } else {
            throw new \Excpetion('Invalid IP: ' . $asString);
        }
    }

    /**
     * gibt die erste adresse eines netzwerkes zurück.
     * @param \Network $network
     * @return \NetworkAddress
     */
    static public function getFirstHost(\SkyBurner\StoreBundle\Entity\Network $network) {
        return self::generateNetworkAddressByInteger($network->getNetaddressNetworkAddress()->getAsInteger() + 1);
    }

    /**
     *
     * @param int $cidr
     * @return \NetworkAddress
     */
    public function subnetMask($cidr) {
        $asInt = bindec(str_pad(str_repeat(1, self::fixCIDR($cidr)), 32, 0, STR_PAD_RIGHT));
        return self::generateNetworkAddressByInteger($asInt);
    }

    /**
     *
     * @param \NetworkAddress $netmask
     * @return int
     */
    public function subnetMaskToCidr(NetworkAddress $netmask) {
        return 32 - log(($netmask->getAsInteger() ^ self::MAX_IP_AS_INT) + 1, 2);
    }

    /**
     *
     * @param \NetworkAddress $ip
     * @param \NetworkAddress $netmask
     * @return \NetworkAddress
     */
    public function netAddress(NetworkAddress $ip, NetworkAddress $netmask) {
        $asInt = $ip->getAsInteger() & $netmask->getAsInteger();
        return self::generateNetworkAddressByInteger($asInt);
    }

    /**
     *
     * @param \NetworkAddress $netAddress
     * @param \NetworkAddress $netmask
     * @return \NetworkAddress
     */
    public function broadcast(NetworkAddress $netAddress, NetworkAddress $netmask) {
//        $ipAsLong = $netAddress->getAsInteger() | (~ $netmask->getAsInteger());
        $ipAsLong = $netAddress->getAsInteger() | $this->flipBin($netmask->getAsInteger());
        return self::generateNetworkAddressByInteger($asInt);
    }
    protected function flipBin($number) {
        $bin = str_pad(base_convert($number,10,2), 32, 0, STR_PAD_LEFT);
        for($i = 0; $i < 32; $i++) {
            $bin{$i} = $bin{$i} === '0' ? '1' : '0';
        }
        return bindec($bin);
    }
    /**
     *
     * @param int $cidr
     * @param \NetworkAddress $ip
     * @return \NetworkAddress
     */
    public function minIp($cidr, NetworkAddress $ip) {
        $netmask = $this->subnetMask($cidr);
        $netAddress = $this->netAddress($ip, $netmask);
        return self::generateNetworkAddressByInteger($netAddress->getAsInteger() + 1);
    }

    /**
     *
     * @param int $cidr
     * @param \NetworkAddress $ip
     * @return \NetworkAddress
     */
    public function maxIp($cidr, NetworkAddress $ip) {
        $netmask = $this->subnetMask($cidr);
        $netAddress = $this->netAddress($ip, $netmask);
        $broadcast = $this->broadcast($netAddress, $netmask);
        return self::generateNetworkAddressByInteger($broadcast->getAsInteger() - 1);
    }

    /**
     * @param int $cidr
     * @return int
     */
    public function countByCidr($cidr) {
        return max(0, pow(2, 32 - self::fixCIDR($cidr)) - 2);
    }

}

Zu beachten ist hier die interne verwendung von "FLOAT", da es bei "INT" probleme geben kann dass wir ein Überlauf erzeugen. Also solltet ihr diese Daten in die Datenbank speichern wollen achtet darauf ein "UNSIGNED INT" zu benutzen! Aus gleichem Grund habe ich eine Reimplementierung vom "Bitweisem NOT (~)" erstellt -> "flipBin".
Ich denke das ganze noch in ein passenden Namespace packen und gut is. Wer hierzu noch ein paar vorschläge hat, immer her damit!

Mittwoch, 19. September 2012

Freeradius: Account gültig ab?

Da hat es mich doch glatt in die tiefen von FreeRadius 2 verschlagen. Aufgabenstellung war, ein Account erst zum Zeitpunkt X als gültig zu erklären. Klingt eigentlich recht simple, hat aber viele Nerven gekostet.
Ausgangssituation; ein komplett durch konfigurierter FreeRadius inkl. verwendeten SQL-Modul, welches über den "authorize_check_query" uns alle zu überprüfenden Kriterien ausgibt. so habe ich für mein vorhaben ein neues Attribute geschaffen: "Activate-Account-At" dies ist ein Unix-Timestamp und soll den Zeitpunkt beschreiben ab wann der Account erst gültig ist.
Zuerst müssen wir FreeRadius dieses neue Attribute bekannt machen und so tragen wir es im "dictionary" ein:

ATTRIBUTE    Activate-Account-At        3080    integer

nun weiß FreeRadius dass es sich hier um ein "Integer" handelt und kann entsprechend mit umgehen. nun müssen wir dieses Attribute nur noch auswerten. Leider gibt es kein Modul was dies für uns erledigen könnte und so bedienen wir uns einfach der im FreeRadius verwendete "un-lang". In der Section "authorize" prüfen wir nun ob unser Attribute überhaupt gesetzt wurde und vergleichen es mit der build-in Variable von FreeRadius "%l" welche den aktuellen Unix-Timestamp enspricht.

authorize {
  ...
    if("%l" && "%{check:Activate-Account-At}") {
        if("%l" < "%{check:Activate-Account-At}") {
            reject
        }
    }
  ...
}

wie zu sehen ist, verwenden wir bei unserem neuen Attribute einen Präfix, der sagt FreeRadius, dass er das gewünschte Attribute aus unseren "check"-Attributen nehmen soll.
Und schon ist unsere neue Regel fertig, war gar nicht mal so wild ;)
Ich habe an dieser Stelle erstmal aufgehört weiter zu basteln, was mir an dieser stelle besser gefallen würde, wenn anstelle eines "Unix-Timestamp" ein "DateTime"-Object verwendet werden könnte. FreeRadius selbst hat auch eine "date" interpretation für Attribute, leider habe ich noch nicht das passende gegenstück gefunden was mir dann per "un-lang" ein gültigen Vergleich liefern könnte. Wer hier eine gute Idee hat, darf hier gerne ein Kommentar hinterlassen.
Alternativ könnte man den vergleich per SQL machen, ist aber meiner meinung nach auch nicht die feine englische Art hier den DB-Server mit zu nerven.

Freitag, 31. August 2012

Zeitberechnung

Heute habe ich mal wieder Feuer gemacht! Ein kleiner Schnipsel Code welcher ermöglicht zB unter JavaScript über die angabe von Sekunden eine genaue Angabe zu machen was dieser Wert in Sekunden/Minuten/Stunden/Tage/Wochen/Monate/Jahre ist:

var elapsed = (((((ts) / 31536000) | 0) > 0) ? (((ts) / 31536000) | 0) + " Jahre " : "") +
((((((ts) / 2592000) % 12) | 0) > 0) ? ((((ts) / 2592000) % 12) | 0) + " Monate " : "") +
((((((ts) / 604800) % 4) | 0) > 0) ? ((((ts) / 604800) % 4) | 0) + " Wochen " : "") +
((((((ts) / 86400) % 7) | 0) > 0) ? ((((ts) / 86400) % 7) | 0) + " Tage " : "") +
((((((ts) / 3600) % 24) | 0) > 0) ? ((((ts) / 3600) % 24) | 0) + " Std " : "") +
((((((ts) / 60) % 60) | 0) > 0) ? ((((ts) / 60) % 60) | 0) + " Min " : "") +
((((((ts)) % 60) > 0) | 0) ? ((((ts)) % 60) | 0) + " Sek" : "");

dies lässt sicht natürlich auch auf jede Andere Sprache anwenden. Der Knackpunkt an diesem Schnipsel ist, dass es durch das Bitweise "ODER" mit "0" ein cast auf ein Integer gibt.

Dienstag, 31. Juli 2012

Bei Symfony 2.1 fehlen Doctrine-Settings

Ich wollte es kaum glauben, aber als ich nach meinem letzten Datenbankumbau im MySQL-Workbench nun mittels Symfony 2.1 CLI ein Datenbank reverse-Engeneering durchführen wollte flog mir Doctrine um die Ohren.

Hier der eigentlich recht simple Aufbau der Problemstellung, zwei Tabellen die jeweils mit zwei M:N Verknüpfungen verwurstet sind.



So, nun Symfony 2.1 mitteilen dass wir gerne daraus Entity-Klassen erstellt haben möchten:

php app/console doctrine:mapping:convert xml ./src/SkyDiablo/StoreBundle/Resources/config/doctrine/metadata/orm --from-database --force

Alles in allem nix besonderes bisher... nur was passiert jetzt. Doctrine wird nun aufgefordert aus der hinterlegten DB, Entitys zu erstellen, was es mit folgender Meldung und mit vollster überzeugung verweigert:

[Doctrine\ORM\Mapping\MappingException]
Property "radUser" in "RadAttribute" was already declared, but it must be declared only once

Was ist passiert? Bei der Erstellung des "RadAttribute"-Entity wollte Doctrine nun für jede M:N Verknüpfung ein Feld anlegen und da die Verknüpfungen auf "RadUser" zeigen, liegt es nahe auch das Feld so zu bennen. Leider haben wir hier zwei Verknüpfungen auf die gleiche Tabelle, was dazu führen würde, dass wir zwei mal das gleiche Feld hätten. Das erkennt Doctrine und streikt an der stelle. Leider bringt uns das kein Meter weiter, keinerlei Übergabeparameter oder Configs im "Doctrine-Bundle" ermöglichen uns hier abhilfe zu schaffen.
Da ich aber öffters über diesen Weg meine Entitys erstelle und ich somit darauf angeweisen bin ging für mich das große Suchen los. Ich habe etliche Zeilen Source debuggt und mich um den Verstand gegoogelt, nichts brauchbares gefunden. Doch da, nach langem hin und her konnte ich ein Schlupfloch finden... Doch erst nochmal etwas Hintergrundinformationen. Die eigentliche Klasse welche mir den Kopf zerbrochen hat, ist diese hier:

Doctrine\ORM\Mapping\Driver\DatabaseDriver

 In ihr werden über die function "loadMetadataForClass" die Feldnamen definiert. Dort habe ich nun gesucht wie ich meinem Problem abhilfe schaffen kann und konnte recht schnell feststellen dass die Feldnamen über die function "getFieldNameForColumn" generiert werden. Und hier gibt uns Doctrine nun auch endliche die Chance selbst Feldnamen zu vergeben! Überglücklich über meinem Fund war auch schon das nächste Problem da, wie sag ich nun Doctrine dass ich an dieser stelle gerne alternative Feldnamen verwenden möchte. Wie bereits gesagt, von seiten Symfony bzw dem Doctrine-Bundle habe ich keine Möglichkeit gefunden.
Nach weiterem langen Gesuche bin ich auf folgendes gestoßen, die Klasse "DatabaseDriver" wird bei Doctrine als eine sogenannte "Metadata-Driver-Implementierung" genutzt und über die Klasse "Doctrine\ORM\Configuration" verwaltet, also vorgehalten. Im Doctrine-Bundle wird das ganze in der Klasse "Doctrine\Bundle\DoctrineBundle\Command\ImportMappingDoctrineCommand" in der Function "execute" so zusammengesteckt:

$em = $this->getEntityManager($input->getOption('em'));

$databaseDriver = new DatabaseDriver($em->getConnection()->getSchemaManager());
$em->getConfiguration()->setMetadataDriverImpl($databaseDriver);

Und weiter keinerlei Möglichkeit auf den "$databaseDriver" zuzugreifen um eventuell alternative Feldnamen zu hinterlegen. Nun kommt der Clue an der geschichte, die Klasse "Doctrine\ORM\Configuration" wird im Doctrine-Bundle über eine Parameter in der "orm.xml" definiert:

<parameters>
        <parameter key="doctrine.orm.configuration.class">Doctrine\ORM\Configuration</parameter>

Hier  habe ich nun angesetzt und diesen Parameter überschrieben indem ich ein neuen gleichnamigen Parameter angelegt habe und somit diese Definition überschrieben habe und dann meine eigene Configuration-Class implementierung angeben habe. In jener habe ich nun die Function "setMetadataDriverImpl" überschrieben und schon hatte ich endlich zugriff auf die gewünschte Klasse und konnte nun so Doctrine mitteilen, dass ich gerne alternative Feldnamen nutzen möchte. Der einfachhalber habe ich alles hard-codiert hinterlegt, könnte man aber natürlich noch über eigene Konfigurations-möglichkeiten im eigenen Bundle durchaus erweitern. In der Praxis sieht das nun so aus:

namespace SkyDiablo\Doctrine\ORM\ConfigurationExtenderBundle\Controller;

use Doctrine\Common\Persistence\Mapping\Driver\MappingDriver;

class Configuration extends \Doctrine\ORM\Configuration {

    public function setMetadataDriverImpl(MappingDriver $driverImpl) {
        parent::setMetadataDriverImpl($driverImpl);

        if ($driverImpl instanceof \Doctrine\ORM\Mapping\Driver\DatabaseDriver) {
            /* @var $driverImpl \Doctrine\ORM\Mapping\Driver\DatabaseDriver */
            $driverImpl->setFieldNameForColumn("rad_user_has_rad_check_attribute", "rad_user_id", "radUserCheck");
            $driverImpl->setFieldNameForColumn("rad_user_has_rad_reply_attribute", "rad_user_id", "radUserReply");
            $driverImpl->setFieldNameForColumn("rad_user_has_rad_check_attribute", "rad_attribute_id", "radAttributeCheck");
            $driverImpl->setFieldNameForColumn("rad_user_has_rad_reply_attribute", "rad_attribute_id", "radAttributeReply");

        }
    }

}

Nun werden die Entitys problemlos erstellt und bekommen jeweils eigenständige Feldnamen! Sollte ich mit meinem Aufwand hier voll daneben liegen und es gibt doch schon eine Lösung mit Boardmitteln, bin ich über jeden Kommentar dankbar!

EDIT: 13.08.2012

Ein alternativer weg, ist über die generelle Namens-Convention von Doctrine zu arbeiten. Das geht zwar etwas hier am Ziel vorbei, lässt aber dennoch das geleiche ergebnis mit sich bringen. Eine entsprechende Doku ist hier zu finden:

http://developmentwithart.com/2012/07/04/solving-the-sql-naming-dilemma-by-using-doctrine2-naming-strategy/

EDIT: 18.06.2013

Habe eben beim Durchstöbern meiner Posts das hier noch gefunden, könnte eventuell auch nochmal abhilfe schaffen. Hatte aber bisher noch keine Zeit mich damit zu beschäftigen:

https://github.com/doctrine/doctrine2/pull/241


Greez,
  hoessi...