Kategorien
Agavi Debugging Frameworks Linux PHP Shell XML

Agavi Custom Logger implementieren

Um in Agavi custom logfiles zu erstellen, benötigt man einen eigens erstellten logger + appender in der app/config/logger.xml.

<!-- logs only custom messages in a custom log -->
<logger name="custom" class="AgaviLogger" level="'custom'">
    <appenders>
        <appender>CustomLogAppender</appender>
    </appenders>
</logger>

<appender name="CustomLogAppender" class="AgaviFileLoggerAppender" layout="DateTimeLayout">
    <ae:parameters>
        <ae:parameter name="file">%core.app_dir%/log/custom.log</ae:parameter>
    </ae:parameters>
</appender>

Dann kann man den Logger quasi überall wo der LoggerManager verfügbar ist benutzen.

$message = 'Custom logging message';
$this->getContext()->getLoggerManager()->log(new AgaviLoggerMessage($message, 'custom'), 'custom');

Und in der bash das logfile einsehen.

tail -f app/log/custom.log
Kategorien
Frameworks PHP SOAP Zend

PDF Dokumente mit Zend LiveDocx

Ein sehr interessanter Ansatz zur Erzeugung von Dokumenten im Web wird bei phphatesme.com beschrieben. HTML-zu-PDF-Konvertierung und programmatischer Ansatz sind ja bekannt. Der neue Ansatz bedient sich einer SOAP Schnittstelle und Templates. Unter folgender URL gibts mehr dazu:

phphatesme.com – PDF Dokumente mit Zend LiveDocx

Kategorien
Debugging PHP

PHPBench – PHP auf dem Prüfstand

Hier der Link zu PHPBench. Benchmarks für PHP Funktionen.

phpbench.com

Kategorien
Debugging Linux PHP PHPUnit Server Shell XDebug

Debugging mit XDebug und KCachegrind

XDebug ist das sehr beliebte und allseits bewährte Tool, das nicht nur Stacktrace und var_dumps ausgeben kann, sondern ebenso für CodeCoverage, echtes Debuggen, Profiling und Testen mit PHPUnit gedacht ist.

XDebug kann Profiler Dateien erzeugen, die man mit KCacheGrind perfekt auswerten kann. So bleibt kein Flaschenhals in der Applikation unbemerkt.

xdebug.org
kcachegrind.sourceforge.net

Kategorien
Agavi Frameworks PHP XML

Eigene Configs in Agavi nutzen

Für diverse Aufgaben in größeren Projekten ist es notwendig, eigene XML-Konfigurationsdateien zu nutzen. Die Definition umfangreicher Einstellungen in der settings.xml wird schnell anstrengend und das Holen der Werte per AgaviConfig::get('setting.name', 'default'); unübersichtlich. Agavi bietet es aber an, eigene Konfigurationsdateien zu erstellen und diese nicht nur zu validieren, sondern auch zur Wiederverwendung zu cachen.
In der Agavi FAQ habe ich dazu mal ein Beispiel verfasst.

Einfach einen Config-Handler in der config_handlers.xml definieren:

<handler pattern="%core.config_dir%/project/foo/*.xml" class="AgaviReturnArrayConfigHandler" />

und dann direkt in den Actions, Views und Models benutzen:

if (!isset($config['baz']))
{
    throw new LogicException('baz is missing!');
}

if (isset($config['bars']))
{
    foreach ($config['bars'] as $foo => $data)
    {
        $this->doSomethingWithEach($foo, $data);
    }
}

Die XML-Datei für den obigen Code könnte beispielweise so aussehen:

<ae:configurations>
    <ae:configuration>
        <baz>asdf</baz>
        <foo>-1</foo>
        <bars>
            <some>...more data and xml...</some>
            <more>...more data and xml...</more>
            <deep>...more data and xml...</deep>
            <structs>...more data and xml...</structs>
        </bars>
    </ae:configuration>
    <ae:configuration environment="production.*">
        <baz>bleh</baz>
        <foo>1</foo>
    </ae:configuration>
</ae:configurations>

Gut zu erkennen ist der Vorteil eigener Configs: Man kann die Agavi-Features für Konfigurationsdateien nutzen und auf einfache Art und Weise environmentspezifische Einstellungen vornehmen (z.B. in „Production“ andere Werte für Logdateien, URLs oder Einstellungen nutzen als während der Entwicklung oder beim Testen).

Ebenfalls gut zu erkennen im PHP-Code ist, dass man lauter Abfragen über die Existenz bestimmer Elemente macht, die man sich mit vernünftiger Validierung sparen kann, da invalide XML-Dokumente gar nicht erst als korrekt angesehen werden von Agavi. Um seine XML-Strukturen zu validieren definiert man per validation Parameter ein XML-Schema und definiert noch gleich einen eigenen ConfigHandler, den man schreibt und per autoload.xml bekannt macht:

<handler pattern="%core.config_dir%/project/foo/*.foo.xml" class="FooDefinitionConfigHandler">
    <validation>%core.config_dir%/xsd/foo_definition.xsd</validation/>
</handler>

Der ConfigHandler könnte so beginnen:

class FooDefinitionConfigHandler extends AgaviXmlConfigHandler
{
    // notwendige Methoden implementieren und XML-Struktur validieren...
}

Für Beispiele einfach in die in Agavi verwendeten XSDs und entsprechende ConfigHandler sehen. Viel Spaß. :)

Kategorien
PHP PHPUnit

PHPUnit Manual – Sebastian Bergmann

Hier der komplette PHPUnit Guide um entsprechende Tests zu seiner Applikation zu schreiben.

PHPUnit Manual

Kategorien
MySQL PHP PHPUnit

Professionelle Softwareentwicklung mit PHP 5

Der ultimative Guide für Objektorientierung, Entwurfsmuster und Modellierung sowie fortgeschrittene Datenbankprogrammierung von Sebastian Bergmann

Professionelle Softwareentwicklung mit PHP 5

Kategorien
Frameworks Javascript Kohana PHP

der Kohana Script Collector Helper

Um modular und agil in Kohana zu entwickeln, wurde ein Skript Kollektor notwendig, der aus allen Controllern (Template- oder Standard-Controllern) Skripte (CSS, Javascript) sammeln kann.

Diese Scripte werden dann auf den jeweiligen Mastertemplates wieder an den richtigen Stellen eingebunden.
Dazu habe ich einen neuen Helper unter application/helpers/collector.php eingerichtet.


class Collector_Core
{
    /**
     * Arrays containing URL's to scripts/styles (fill with standards)
     * @var string
     */
    static protected $scripts       = array();
    static protected $styles        = array();

    /**
     * Adds a url to store
     * @param string $file the local path to file
     * @return void
     */
    static public function addJs($file)
    {
        self::$scripts[] = $file;
    }

    /**
     * Adds a url to store
     * @param string $file the local path to file
     * @return void
     */
    static public function addCss($file)
    {
        self::$styles[] = $file;
    }

    /**
     * Generates/renders collectors items
     * @param boolean      $print whether to echo the output or just return rendered string
     * @return string      the rendered output
     */
    static public function renderJs($print = false)
    {
        $scripts    = array_unique(self::$scripts);
        $output     = html::script($scripts);
        if ($print)
        {
            echo $output;
        }
        else
        {
            return $output;
        }
    }

    /**
     * Generates/renders collectors items
     * @param boolean      $print whether to echo the output or just return rendered string
     * @param string|array $media type for this style (all, screen, print, media)
     * @return string      the rendered output
     */
    static public function renderCss($print = false, $media = 'all')
    {
        $styles = array_unique(self::$styles);
        $output = html::stylesheet($styles, $media);
        if ($print)
        {
            echo $output;
        }
        else
        {
            return $output;
        }
    }
} // end of Collector_Core

Dieser Helper kann nun aus allen Controllern heraus befüllt werden.


class Welcome_Controller extends Template_Controller
{
    /**
     * set master template
     */
    public $template = 'master_default.tpl';

    /**
     * default constructor
     * @param void
     * @return void
     */
    public function __construct()
    {
        // load parent constructor
        parent::__construct();

        // collect scripts and styles
        collector::addCss('/css/fancybox');
        collector::addJs('/js/jquery.1.3.2');
        collector::addJs('/js/jquery.fancybox');
    }
    /** more code here */
} // end of Welcome_Controller

Nachdem nun alle relevanten Skripte eingesammelt wurden, kann man diese auf dem Template wieder ausgeben lassen.


<?php collector::renderCss(true, 'all'); ?>

<!-- html code here -->

<?php collector::renderJs(true); ?>

Der Kollektor sorgt dafür das keine doppelten Skripte geladen werden.

Kategorien
Kohana PHP SOAP Zend

SOAP Service mit Kohana und Zend AutoDiscover

Eine einfache all-in-one Lösung für Standard SOAP Services kann man mit dem Kohana Framework und der Zend Bibliothek realisieren.

Dazu bedarf es lediglich eines Kohana Frontcontrollers über den wir den Service und die WSDL ansprechen können.

Zur automatischen Generierung der WSDL, bedienen wir uns hier der Zend AutoDiscover Klasse. Dieser Klasse übergibt man lediglich die ServiceModel-Klasse mit enthaltenen Annotationen, die die Servicefunktionen enthält.

Aus den Annotationen generiert Zend AutoDiscover eine passende WSDL.


/**
 * include libs and models
 *
 */
include('Zend/Soap/AutoDiscover.php');
include(APPPATH . 'models/service.php');

/**
 * this class represents a controller
 * application/controllers/soap.php
 *
 * @package     SOAPService
 * @subpackage  ...
 * @author      saegefisch (xxx@xxx.xx)
 * @copyright   (c) 2009 xxx
 */
class Soap_Controller extends Controller
{
    /**
     * default constructor
     *
     * @param void
     * @return void
     */
    public function __construct()
    {
        // load parent constructor
        parent::__construct();
    }

    /**
     * service to call
     *
     * @param void
     * @return void
     */
    public function service()
    {
        // disable wsdl cache
        ini_set('soap.wsdl_cache_enabled', '0');

        // set auth settings if needed
        $settings   = array(
                          'login'              => 'user',
                          'password'        => 'password',
                          'authentication' => SOAP_AUTHENTICATION_BASIC,
                          'soap_version'   => SOAP_1_2,
                          'encoding'         => 'UTF-8',
                          'cache_wsdl'     => WSDL_CACHE_NONE
                          );

        // include user:password if needed
        $wsdl = 'http://user:password@' . $_SERVER['HTTP_HOST'] . '/soap/wsdl';
        $server = new SoapServer($wsdl, $settings);
        $server->setClass('Service_Model');
        $server->handle();
    }

    /**
     * wsdl to call
     *
     * @param void
     * @return void
     */
    public function wsdl()
    {
        // disable wsdl cache
        ini_set('soap.wsdl_cache_enabled', '0');

        $wsdl = new Zend_Soap_AutoDiscover();
        $wsdl->setUri('http://' . $_SERVER['HTTP_HOST'] . '/soap/service');
        $wsdl->setClass('Service_Model');
        $wsdl->handle();
    }
}

Hier stellen wir das Standard Model für unseren SOAP Service zusammen.


/**
 * this class represents a model
 * application/models/service.php
 *
 * @package     SOAPService
 * @subpackage  ...
 * @author      saegefisch (xxx@xxx.xx)
 * @copyright   (c) 2009
 */
class Service_Model extends Model
{
    /**
     * default constructor
     *
     * @param   void
     * @return  void
     */
    public function __construct()
    {
        // load database library into $this->db (can be omitted if not required)
        parent::__construct();
    }

    /**
     * dummy function
     *
     * @param   int $int
     * @param   string $string
     * @param   array $arr
     * @param   object $obj
     * @param   bool $bool
     * @return  array
     */
    public function get_dummy_array($int, $string, $arr, $obj, $bool)
    {
        return array();
    }

    /**
     * dummy function
     *
     * @param   int $int
     * @param   string $string
     * @param   array $arr
     * @param   object $obj
     * @param   bool $bool
     * @return  bool
     */
    public function get_dummy_boolean($int, $string, $arr, $obj, $bool)
    {
        return true;
    }

    /**
     * dummy function
     *
     * @param   int $int
     * @param   string $string
     * @param   array $arr
     * @param   object $obj
     * @param   bool $bool
     * @return  string
     */
    public function get_dummy_string($int, $string, $arr, $obj, $bool)
    {
        return 'foo=bar';
    }
}

Kategorien
MySQL PHP Propel ORM

der Criteria Builder für Propel ORM Criterias

propel_criteria_builder

Die Propel ORM für PHP bedient sich einer eigenen Syntax (Criterias) um Queries zusammenzustellen.

Hier gibt es einen Criteria Builder, den man mit Standard SQL füttern kann.

Der Criteria Builder konvertiert sodann das eingegebene SQL Statement in ein Propel Criteria.

So wird aus diesem kleinen SQL Statement:


SELECT user.*
FROM user
WHERE user.state = 100 
AND (user.name = 'user' OR user.email = 'user@domain.tld')

diese Propel Criteria:


$c = new Criteria();
$crit0 = $c->getNewCriterion(UserPeer::STATE, 100);
$crit1 = $c->getNewCriterion(UserPeer::NAME, 'user');
$crit2 = $c->getNewCriterion(UserPeer::EMAIL, 'user@domain.tld');

// Perform OR at level 1 ($crit1 $crit2 )
$crit1->addOr($crit2);

// Perform AND at level 0 ($crit0 $crit1 )
$crit0->addAnd($crit1);

// Remember to change the peer class here for the correct one in your model
$c->add($crit0);
$result = TablePeer::doSelect($c);

// This loop will of course need to be edited to work
foreach ($result as $obj)
{
	//$val = $obj->getValue();
}

Einen Versuch ist es Wert.

Hier gehts zum Propel ORM Criteria Builder