PHP Errors und Zend_Log incl. Mail verheiraten

Über die Bootstrap hab ich mir meinen Logger konfiguriert und in die Registry abgelegt. Ungefähr so:

public function _initLogger()
{        
    $configIni = new Zend_Config_Ini(APPLICATION_PATH . '/configs/application.ini', $this->getEnvironment());

    // Konsolenlog initialisieren
    $writerStream = new Zend_Log_Writer_Stream('php://output');
    $writerStream->addFilter((int) $configIni->log->console->level);

    // Mail-Logging initialisieren
    $configMail = array(
        'auth'      => 'login',
        'username'  => $configIni->mail->smtp->user,
        'password'  => $configIni->mail->smtp->pass,
        'ssl'       => 'tls',
        'port'      => $configIni->mail->smtp->port
    );

    $smtp = new Zend_Mail_Transport_Smtp($configIni->mail->smtp->host, $configMail);
    $mail = new Zend_Mail('utf-8');
    $mail->setFrom($configIni->log->mail->from)
         ->addTo($configIni->log->mail->to)
         ->addCc($configIni->log->mail->cc)
         ->setSubject('Fehlermeldungen:')
         ->setDefaultTransport($smtp);

    $writerMail = new Zend_Log_Writer_Mail($mail);
    $writerMail->addFilter((int) $configIni->log->mail->level);

    // Writer zum Logger hinzufügen
    $logger = new Zend_Log();
    $logger->addWriter($writerStream);
    $logger->addWriter($writerMail);
    Zend_Registry::set('logger', $logger);
}

Das entsprechende PHP Script required die index.php und bootstrapped so das Zend Framework. Das Script soll im Fehler bzw. Abbruchfall einen Fehler in den Logger schreiben und ebenfalls die Logger-Mail versenden.

require_once __DIR__ . '/index.php';
declare(ticks=1);

function shutdown()
{
    if ($error = error_get_last()) {
        if (isset($error['type']) && ($error['type'] == E_ERROR ||
                $error['type'] == E_PARSE ||
                $error['type'] == E_COMPILE_ERROR)
        ) {
            ob_end_clean();

            $message = 'Es ist ein Fehler aufgetreten: ' . "\n"
                . 'Message: ' . $error['message'] . "\n"
                . 'File: ' . $error['file'] . "\n"
                . 'Line: ' . $error['line'];
            /* @var $logger Zend_Log */
            $logger = Zend_Registry::get('logger');
            $logger->log($message, Zend_Log::CRIT);
            $logger->__destruct();
        }
    }
}

//// signal handler function
function sig_handler($signo)
{
    switch ($signo) {
        case SIGTERM:
        case SIGINT:
        case SIGHUP:
        case SIGUSR1:
            $message = 'Es ist ein Fehler aufgetreten';
            /* @var $logger Zend_Log */
            $logger = Zend_Registry::get('logger');
            $logger->log($message, Zend_Log::CRIT);
            $logger->__destruct();
            exit;
        default:
            // handle all other signals
    }
}

register_shutdown_function('shutdown');

Hervorzuheben und wichtig sind hier die Zeilen declare(ticks=1); sowie $logger->__destruct();. Mit dem Destruktor vom Logger initialisiert ihr das Versenden der Mail, da das Script selber nicht korrekt beendet wurde und die destruct-Funktion vom Logger somit auch nicht aufgerufen wurde.

Zend_Form + Autocomplete

Da ich im Netz kein zufriedenstellendes Beispiel für Zend_Form in Verbindung mit Autocomplete finden konnte, möchte ich hier meinen Lösungsansatz zeigen. Vorraussetzungen sind hierfür ein Zend_Form, sowie aktiviertes jQuery + jQueryUI.

Im Formular passiert erstmal nix Besonderes. Es wird ein normales Zend_Form_Element_Text angelegt.

$autoCompleteElement = new Zend_Form_Element_Text('autoCompleteElement');

Der Controller bekommt eine zusätzliche autocompleteAction spendiert, welche wir dann später über jQuery ansprechen wollen, um die Werte zur aktuellen Eingabe zu erhalten. Wichtig ist hier, die Werte JSON-kodiert zu übergeben, um sie später im Javascript-File sauber auslesen zu können. Bei meinem $returnArray soll der Value später ins Formularfeld eingetragen werden, wohingegen das Label als Anzeige für die Auswahl beim Autocomplete gedacht ist.

public function autocompleteAction()
{
    $post = $this->_request->getPost();
    $searchString = $post['searchString'];
    
    // $returnArray zur Demonstration meiner übergebenen Daten
    $returnArray[$row['value']] = $row['label']; 
    $valuesJson = Zend_Json::encode($returnArray);
    echo $valuesJson;
    $this->view->layout()->disableLayout();
    $this->_helper->viewRenderer->setNoRender(true);
}

Das Javascript kümmert sich schlussendlich um das Absenden des Requests und das Mappen der empfangenen Daten.

$(document).ready(function() {
    $("input#autoCompleteElement").autocomplete({
        source : function( request, response) {
            url = "/meinModul/meinController/autocompleteAction";
            data = { 'searchString' : request.term };
            $.post(url, data, function(JSON){
                response($.map(JSON, function (item, value) {
                    return {
                        label: item,
                        value: value,
                        source: item};
                    }));
            }, 'json');
        },
        minLength: 1,
        autoFocus: true
    });
});

Zum Schluss natürlich nicht vergessen das Javascript in euer View-Script mit einzubinden.

Zend_Form mit mehreren Submit-Buttons

Benötigt man für ein Zend Formular mehrere Ausgänge, so bieten sich folgende Lösungen an:

  • Buttons, die per Javascript Aktionen auslösen
  • mehrere Submitbuttons

Variante 2 lässt sich recht einfach umsetzen. Zunächst ein Blick in den interessanten Teil des Formulars:

$delete = new Zend_Form_Element_Submit('delete');
$delete->setLabel('Löschen');

$duplicate = new Zend_Form_Element_Submit('duplicate');
$duplicate->setLabel('Duplizieren');

$submit = new Zend_Form_Element_Submit('submit');
$submit->setLabel('Absenden')

$this->addElements(array($delete, $duplicate, $submit));

Wir haben also drei verschiedene Submitbuttons angelegt und diese dann dem Formular zugewiesen. Als nächstes schauen wir uns die Formularverarbeitung bspw. im Controller an:

$form = new MyNewForm();
if ($this->getRequest()->isPost()) {
    $formData = $this->getRequest()->getPost();

    if ($form->submit->isChecked() 
        && $form->isValid($formData)) {
        // Form valide und Submitbutton wurde gedrückt
    }

    if ($form->delete->isChecked() 
        && $form->isValid($formData)) {
        // Form valide und Delete-Button wurde gedrückt
    }

    if ($form->duplicate->isChecked() 
        && $form->isValid($formData)) {
        // Form valide und Duplicate-Button wurde gedrückt
    }
}

Wir prüfen also zunächst, ob das Formular abgeschickt wurde, danach ob das Formular valide ist und der jeweilige Button gedrückt wurde. Über die Methode isChecked() des jeweiligen Formularelements bekommen wir jetzt recht simpel heraus, ob der Button gedrückt wurde.

[via]

Zend_Db_Select mit fehlender Variable in WHERE-Clause

Nehmen wir folgenden Anwendungsfall an:

$userSelect = new Zend_Db_Select($db);
$userSelect->from('User')
           ->where('Name = ?', $name);

Im Normalfall sollte die Query wie folgt aufgelöst werden:

WHERE Name = 'MeinName'

Für den Fall, dass $name = NULL, also es nicht übergeben wird, erzeugt Zend folgendes Query, welches folglich einen SQL Fehler wirft:

WHERE NAME = ?

Um diesen Fall zu vermeiden, castet man die Variable in den Datentyp, den das Tabellenfeld hat, bspw. in einen String:

->where('Name = ?', (string) $name);

Ist nun $name = NULL, so erhalten wir dennoch eine korrekte Syntax:

WHERE Name = ''

Cheat-Sheet für Zend application.ini

Mittels der application.ini in Zend Framework Projekten kann man seine Zend-Anwendung auf recht simple Art konfigurieren. Doch nur die Wenigsten kennen alle Konfigurationsmöglichkeiten der application.ini. Aus diesem Grund hat Florian Eibeck ein, aus meiner Sicht sehr sinnvolles, Cheat-Sheet für eben diese Konfigurationsdatei bereit gestellt, welche eben alle Möglichkeiten aufzeigt. Aktuell basiert die Datei auf Zend Framework 1.11.11 und wird ständig weiter gepflegt.

Link: Zend Framework: Cheat-Sheet application.ini

Versteckte Dateien in Eclipse und Zend Studio anzeigen

Möchte man im Eclipse oder Zend Studio eine .htaccess Datei bearbeiten, so wird man diese im PHP-Explorer oder im Navigator standardmäßig nicht finden. Da die Datei mit einem „.“ beginnt gilt sie als versteckte Datei und wird ausgeblendet.

Um diese versteckten Dateien dennoch wieder einzublenden, klickt ihr im PHP-Explorer auf das kleine Dreieck und wählt den Menüpunkt „Filters …“ aus. Im sich öffnenden Fenster muss der Haken bei „.* resources“ entfernt werden, damit zukünftig auch die versteckten Dateien angezeigt werden.

Zend_Db_Select mit IN in WHERE-Clause

Möchte man über Zend_Db_Select in einer WHERE-Clause das IN benutzen, so muss man die für das IN übergebenen Werte nicht selber per implode() auseinander klamüsern. Dies kann das Zend_Db_Select für euch übernehmen. Siehe dazu nachfolgendes Beispiel:

$data = array(1,3,4);
$select->where('status_id IN(?)', $data);

Gefunden via Stackoverflow