<![CDATA[Robo47 Content Feed (Blog, Codeschnipsel, Texte, Tools)]]> http://www.robo47.net Sat, 25 Oct 2014 02:45:08 +0000 robo47@robo47.net (Benjamin Steininger) robo47@robo47.net Zend_Feed de-de http://blogs.law.harvard.edu/tech/rss <![CDATA[What can go wrong on switching application-context in symfony 1.4]]> http://www.robo47.net/blog/216-What-can-go-wrong-on-switching-application-context-in-symfony-1.4 http://www.robo47.net/blog/216-What-can-go-wrong-on-switching-application-context-in-symfony-1.4 working branch of the base applications backend. Most parts of this backend were based on the admingenerator. Each plugin hooks itself into the backend, includes menus, configuration and has generator based CRUDs for its models.

Listing, editing, creating new entries all no problem. But deleting wasn't possible in the list-view, it always showed me the errorpage of symfony, deleting an entry from the edit-page worked.

Switching to dev-mode showed me a csrf-warning. I cleared cleared and reset everything related to the application, deleted the sessions, restarted all services related, reset the database, created a new entry and tried to delete it ... again failing.

Last point where it worked: before the holidays. Since I was the only developer touching the code, there wasn't anybody else to blame.

Switching to another plugins generators, some had the bug, some didn't, now it was really getting weird, since all use the same modified theme as base, configurations was nearly the same.

After hacking around with the generator.yml, trying everything and not getting anywhere I choose to just go back in time. Thanks to git aka version control, I went back commit by commit in a plugin which had the problem and it showed me where the problem was located.

The problem was introduced by what looked like a simple feature short before the holidays: Linking from the admingenerator-list to the entries in the frontend by using the frontends context.

After finding Accessing the frontend routing from the backend application and modifiying it a bit I used it to create columns in some generator lists which fetched the frontend-router and generated the url by using the router.

Everything was perfect, expect, switching back with sfContext::switchTo($current_application); wasn't really enough to switch back to the backend-application. It reset sfConfig and some other things, but doesn't call sfApplicationConfiguration->initConfiguration(); which usally runs
if (false !== sfConfig::get('sf_csrf_secret'))
{
  sfForm::enableCSRFProtection(sfConfig::get('sf_csrf_secret'));
}
So what you need to do is running an additional
    $current_configuration->initConfiguration();
after running the switchTo, else you application will use the csrf-tokens from the application you switched to and it probably has more sideaffects. ]]>
Wed, 07 Mar 2012 20:04:39 +0000
<![CDATA[WYSIWYG Editor for Markdown and reStructuredText on Linux / Ubuntu]]> http://www.robo47.net/blog/215-WYSIWYG-Editor-for-Markdown-and-reStructuredText-on-Linux-Ubuntu http://www.robo47.net/blog/215-WYSIWYG-Editor-for-Markdown-and-reStructuredText-on-Linux-Ubuntu Markdown or reStructuredText (mainly for use with sphinx), I was looking for a better editor with some kind of live preview for linux. Normally I edited those files directly in Netbeans or Gedit. I found ReText a nice Gui-Editor written in python with QT for the frontend. It does exactly what i wanted, 2 Columns, one with markdown or reStructuredText, the other shows a live preview. For Ubuntu there is a PPA: https://launchpad.net/~mitya57/+archive/ppa which includes integration into the menu and you don't have to create the entry yourself to automatically open files If you don't install via the PPA you have to create the menu-entry yourself to be able to automatically open files with it since Ubuntu seems to no more support opening files with programs which aren't installed in the menu: Add Custom command in the Open with dialog. ]]> Tue, 06 Mar 2012 20:04:39 +0000 <![CDATA[Extending Heise SocialSharePrivacy to pass a dynamic title to twitter]]> http://www.robo47.net/blog/214-Extending-Heise-SocialSharePrivacy-to-pass-a-dynamic-title-to-twitter http://www.robo47.net/blog/214-Extending-Heise-SocialSharePrivacy-to-pass-a-dynamic-title-to-twitter Heise SocialSharePrivacy and experimented around with it (probably will integrate it here via a plugin) but one think I was missing was a way to use it on a list of items, while having different titles for the twitter text, not using the current pages title. Currently it only allows for a static title or a function. But the function was worthless in this case, since I wanted to use the title each specific link. An example based on their ones to describe it a bit more.
<div class="anriss">
    <h3><a href="http://www.heise.de">heise</a></h3>
    <p>lorem ipsum</p>
    <div class="social"></div>
</div>

<div class="anriss">
    <h3><a href="http://www.heise.de/security/">heise security</a></h3>
    <p>dolor sit amet</p>
    <div class="social"></div>
</div>

<script>
$(".social").socialSharePrivacy({
    uri : function(context) {
        return $(context).parents(".anriss").find("h3 a").attr("href");
    }
    service : {
        twitter: {
            'tweet_text': function (context) {
                return $(content).parents(".anriss").find("h3 a").text();
            }
        }
    }
});
</script>
All you need to do to get it working is changing line 196 of the jQuery Plugin from
text = text();
to
text = text(context);
Now you have access to the context inside your function. I just mailed it to Heise, let's see what they thing about it. ]]>
Wed, 07 Dec 2011 20:02:56 +0000
<![CDATA[ReflectionException: Method PHPUnit_Framework_Warning::Warning() does not exist]]> http://www.robo47.net/blog/213-ReflectionException-Method-PHPUnit_Framework_Warning-Warning-does-not-exist http://www.robo47.net/blog/213-ReflectionException-Method-PHPUnit_Framework_Warning-Warning-does-not-exist ReflectionException: Method PHPUnit_Framework_Warning::Warning() does not exist That may be because you are using @dataProvider and your dataProvider returns the data in an invalid way. This may be caused by forgetting one dimension in the returned array when using only a single value as parameter for the test. Wrong:
<?php

public function myDataProvider()
{
    $data = array();

    $data[] = 'some value i want to pass as param';
    $data[] = 'another value i want to pass as param';

    return $data;
}
Right:
<?php

public function myDataProvider()
{
    $data = array();

    $data[] = array('some value i want to pass as param');
    $data[] = array('another value i want to pass as param');

    return $data;
}
]]>
Wed, 30 Nov 2011 19:59:38 +0000
<![CDATA[Netbeans, Symfony, Xdebug and the magical file_link_format]]> http://www.robo47.net/blog/210-Netbeans-Symfony-Xdebug-and-the-magical-file_link_format http://www.robo47.net/blog/210-Netbeans-Symfony-Xdebug-and-the-magical-file_link_format Chromium to open Files in Netbeans directly from symfony1 stack traces, because some time ago in a video I had see how it was done for other editors. Originally I was looking for a solution to get Chromium to open Files in Netbeans directly from symfony1 stack traces, because some time ago in a video I had see how it was done for other editors. Here we have two screenshots for a symfony1 stack trace, which is shown in dev-mode when an error happens or an exception is thrown but not caught and a xdebug stack trace:
  • Symfony1 error strack trace with netbeans links
  • And a xdebug stack trace with netbeans links
For symfony1 to create the links you need the following in settings.yml
all:
  .settings:
    file_link_format:       netbeans://%f?line=%l
For xdebug you need to have html_errors = On in your php.ini set xdebug.file_link_format
xdebug.file_link_format = "netbeans://%f?line=%l"
The next thing we need to do is to create a new url-handler in Gnome's gconf.
gconftool-2 -t string --set /desktop/gnome/url-handlers/netbeans/command "/path/to/netbeansprotocol.sh %s"
gconftool-2 -t bool --set /desktop/gnome/url-handlers/netbeans/enabled true
gconftool-2 -t bool --set /desktop/gnome/url-handlers/netbeans/needs_terminal false
netbeansprotocol.sh will do the parsing - a snippet I found in my desktop-wiki from a search for the same problem some time ago - it probably can be done in a nicer way - but it works (for me).
#!/bin/bash

url=$2
file=${url#*\/\/}
file=${file%?line=*}
line=${url#*line=}

/home/robo47/netbeans-7.0/bin/netbeans --open $file:$line
Further Links: ]]>
Tue, 08 Nov 2011 19:48:59 +0000
<![CDATA[Identifier "request_context" is not defined with silex]]> http://www.robo47.net/blog/211-Identifier-request_context-is-not-defined-with-silex http://www.robo47.net/blog/211-Identifier-request_context-is-not-defined-with-silex If you ever receive a message like:
Fatal error: Uncaught exception 'InvalidArgumentException' with message 'Identifier "request_context" is not defined.' in phar:///.../vendor/silex/silex.phar/vendor/pimple/lib/Pimple.php on line 12
And it is not directly about the AsseticExtension in Silex-Extensions,it is probably because some of your code wants access to the url_generator - in my case it was my own Routing-Extension for twig.

First some background informations why it happens.

The problem is that some Extensions - like the url_generator - need access to the requestContext, which is defined after you have already called $app->run(); so in your bootstrap-code that probably has not happened.

The creation of the RequestContext is inside Silex\Application::onKernelRequest (Line may differ - works current master).

There are too solutions to the problem - either you lazyload the url_generator by passing in the app instead of the url_generator and access it only when you really need it or you let Silex run your code inside Silex\Application::onKernelRequest by putting it in a closure as a callback to add it to the dispatcher as a listener for the SilexEvents::BEFORE-Event - which is triggered inside Silex\Application::onKernelRequest.

]]>
Tue, 08 Nov 2011 19:48:59 +0000
<![CDATA[Automatic pushing to a remote git-repository with etckeeper]]> http://www.robo47.net/blog/212-Automatic-pushing-to-a-remote-git-repository-with-etckeeper http://www.robo47.net/blog/212-Automatic-pushing-to-a-remote-git-repository-with-etckeeper #!/bin/sh set -e git push origin master And don't forget
chmod +x /etc/etckeeper/post-install.d/60vcs-commit-push
]]>
Tue, 08 Nov 2011 19:48:59 +0000
<![CDATA[Logging Mails send via Zend_Mail with Zend_Log using a Mail-Transport]]> http://www.robo47.net/text/43-Logging-Mails-send-via-Zend_Mail-with-Zend_Log-using-a-Mail-Transport http://www.robo47.net/text/43-Logging-Mails-send-via-Zend_Mail-with-Zend_Log-using-a-Mail-Transport Disclaimer: This article is old but was not yet publicated here, but probably somebody can still use those informations

For my blog I wanted a simple way to log all Mails send via Zend_Mail to a File or Database, as fallback if the mailserver went down and for having all contact-mails accessable in the administration-backend too. The possible Ideas I first had were:

  1. Change all the code where I send a mail and add logging by hand with Zend_Log
  2. Extend Zend_Mail with a setLog and overwrite the send-Method
  3. Write a Transport for Logging

First way was out very fast, because it was ugly, ugly, ugly ... and would involve changing a lot of Code and repeating myself (DRY), nothing I really wanted to.

While looking at the other two options I choose the Logging-Transport-version, which meant to write some kind of proxy for the transport too because Zend_Mail only supports one transport at a time and I did not want to only have logging.
So I wrote Robo47_Mail_Transport_Multi and Robo47_Mail_Transport_Log.
Robo47_Mail_Transport_Log supports setting the used priority and using a category for logging, like my extended version of Zend_Log (Robo47_Log) and most other classes I have written which allow logging (Robo47_ErrorHandler, Robo47_Controller_Plugin_Tidy). The Log-Transport supports formatters for the Zend_Mail-Object, I wrote 2 Formatters, a simple one which justs greps Subject, recipients and text/html-content concats it into a string and logs it and a second one which just uses serialize on the Zend_Mail-object.

You can find the code in my Robo47 Components Repository on github.

]]>
Sun, 19 Dec 2010 12:32:06 +0000
<![CDATA[Creating hidden dummy files in empty directories for git]]> http://www.robo47.net/text/42-Creating-hidden-dummy-files-in-empty-directories-for-git http://www.robo47.net/text/42-Creating-hidden-dummy-files-in-empty-directories-for-git When working with symfony and git I often missed empty directories auto generated by symfony on the first commits, which would later be used or needed only for running the app (cache/log), but not having them in vcs for later use annoyed me. So a simple one liner allows you to create an empty .sf-file in each empty directory ignoring empty folders in .git-folders.

find ./ -type d -empty |grep -v '/.git/' | xargs -i touch "{}/.sf"

if you leave away the xargs part (| xargs -i touch "{}/.sf")

find ./ -type d -empty | grep -v '/.git/'  
you can see a list of the empty directories.

Directories where that can happen:

  • test/fixtures/project
  • modules-directoriesi
  • 18n-directories.
  • And if you want to delete all .sf in a directory:

    find ./ -type f -name ".sf" -exec rm {} \;
    
    ]]> Sun, 19 Dec 2010 12:18:48 +0000 <![CDATA[Creating your own git aliases based on your needs]]> http://www.robo47.net/blog/209-Creating-your-own-git-aliases-based-on-your-needs http://www.robo47.net/blog/209-Creating-your-own-git-aliases-based-on-your-needs If you want to create git aliases optimized for your daily work, you should start by analysing your shell logs to find out which git commands you use most of the time and create shortcuts for them:

    bash:

     
    cat .bash_history  | egrep -i "^git" | sort | uniq -c | sort -n
    

    zsh with extended history (including timestamps) using fc instead of cat.

    fc -ln 0 | egrep -i "^git" | sort | uniq -c | sort -n  
    

    sample output:

         31 git checkout master
         41 git submodule foreach "git push origin master"
         59 git submodule foreach "git pull origin master"
        265 git submodule foreach "git gui &"
        358 git status -s
        979 git gui &
    

    Now you can start to write your own aliases for everything and optimize you daily workflow. My aliases can be found here: Git, Aliases, Submodules and Symfony

    ]]>
    Sun, 19 Dec 2010 11:56:31 +0000
    <![CDATA[Git, Aliases, Submodules and Symfony]]> http://www.robo47.net/blog/208-Git-Aliases-Submodules-and-Symfony http://www.robo47.net/blog/208-Git-Aliases-Submodules-and-Symfony Some time ago i stumpled over Joel Perras (@jperras) blogentry Making Git Behave on twitter, covering the .gitconfig file and that was when I first found out about gits aliases and thanks to Joel I started to fill my gitconfig with more and more of them, speeding up my work with git und my working-environment for my mostly symfony based stuff.

    For symfony-projects I am using a lot of submodules in git for symfony itself, all self written plugins, many third-party plugins [sfImageTransformExtraPlugin, sfLessPhpPlugin, ... ] and since some of the commands are long and I have to type them often, when working on the plugins I created my own set of aliases for optimizing my workflow with symfony, git and submodules. For different calls to git submodule foreach, like pulling/pushing all submodules, opening git gui for all submodules which have changes in them and more.

    [alias]
      # Sources:
      #  - Joel Perras ( @jperras http://nerderati.com/2010/07/making-git-behave/ )
      #  - http://www.commandlinefu.com/commands/view/4519/list-all-authors-of-a-particular-git-project
      #  - http://www.jukie.net/bart/blog/pimping-out-git-log
      #  - http://stackoverflow.com/questions/466764/show-ignored-files-in-git
      #  - https://git.wiki.kernel.org/index.php/Aliases
    
      # [s = status] short status
      s = "status -s"
      
      # [b = branch]
      b = "branch -a"
      
      # [g = gui]
      g = "!git gui &"
      
      # [q = qgit]
      q = "!qgit &"
      
      # [m = meld] Opens meld the diff-viewer with current directory
      m = "!meld . &"
      
      # unadd - removes files/directories from staging
      unadd = rm -r --cached
      
      # gitk  
      k = !gitk --all --select-commit=HEAD &
      
      # launches diffuse diff viewer showing modified files
      diffuse = !diffuse --modified
      
      # [l = log] short one line logs with ref-names
      l  = log --oneline --decorate=short
      
      # [l = log] short one line logs with ref-names, date and author - nice colored
      l2 = log --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --date=relative
      
      # [gl = graph log]
      gl = log --oneline --decorate --stat --graph
      
      # [r = remote] listing remotes with urls
      r  = remote -v
      
      # [dfb = diff before pull]
      dfb = diff ORIG_HEAD HEAD
      
      # [sf = submodule foreach] shortcut when running something on all submodules - eg. git sf "git s"
      sf = submodule foreach
      
      # [c = count] number of commits in current branch
      c  = "!git log --oneline | wc -l "
    
      # cc clean and compress the repository [be carefull, can use a lot of RAM and take long on big repositories]
      cc = "!du -hs .git; git gc --aggressive; du -hs .git;"
      
      # [co = checkout]
      co = checkout
      
      # shows ignored directories
      ignored = "!git ls-files --others -i --exclude-standard --directory"
      
      # [ci = commit]
      ci = commit
      
      # [aa = add all] Adds all files
      aa = add .
      
      # [rs = reset hard] Resets modified files to state of last commit
      rh = reset --hard
      
      # [h = hash] hash of HEAD
      h = rev-list --max-count=1 HEAD
      
      # [shc = submodule hash] Shows current sha1s of all submodules and number of commits
      shc = "!git sf 'git h; git c'"
      
      # [llm = last log message] Shows the last git logentry (hash, author, date commitmessage)
      llm = log -1
      
      # [lcm = last log messages submodules] Last log message of all submodules
      llms = !git sf "git llm;"
    
      # [siu = submodule init update] Runs submodule-initialisation and update after a fresh checkout RECURSIVE!
      siu = "!git submodule init; git submodule update; git submodule foreach \"git siu\""
      
      # Pushs all submodules to origin master
      pushsub = submodule foreach "git push origin master"
      
      # Pull submodules from origin master
      pullsub = submodule foreach "git pull origin master"
    
      # [icg = if changed gui] Opens git gui if there are changes in the repository
      icg = "!REPOCHANGED=`git s`; REPOCHANGED=${#REPOCHANGED}; if [ $REPOCHANGED != 0 ]; then git gui & fi;"
      
      # Open git guis for all submodules which have changes
      subgui = "submodule foreach git icg"
      
      # [ul = user list]  show users which have commits in current branch 
      ul = "!git log --format='%aN' | sort -u" 
      
      # [uccl = user commit count list ;)] show users which have commits in current branch, including number of commits, sorted (most commits last)
      uccl = "!git log --format='%aN <%aE>'  | awk '{arr[$0]++} END{for (i in arr){print arr[i], i;}}' | sort -n"
      
      # [ahg = archive head gzip] creates a tar.gz archive named after the last commits hash from HEAD! in the directory above the repository
      ahg = "!git archive HEAD --format=tar | gzip > ../`git h`.tar.gz"
    

    The comments should explain the aliases, if not, just ask.

    Another resource for some aliases is the git wiki on kernel.org: Git Aliases.

    ]]>
    Sun, 19 Dec 2010 11:42:19 +0000
    <![CDATA[RuntimeException: PHP Fatal error: 'You cannot serialize or unserialize PDOStatement instaces' in -:31 - PHPUnit - Process Isolation and Doctrine/PDO]]> http://www.robo47.net/blog/207-RuntimeException-PHP-Fatal-error-You-cannot-serialize-or-unserialize-PDOStatement-instaces-in-31-PHPUnit-Process-Isolation-and-Doctrine-PDO http://www.robo47.net/blog/207-RuntimeException-PHP-Fatal-error-You-cannot-serialize-or-unserialize-PDOStatement-instaces-in-31-PHPUnit-Process-Isolation-and-Doctrine-PDO If you ever get this message:

    RuntimeException: PHP Fatal error:  Uncaught exception 'PDOException' with message 'You cannot serialize or unserialize PDOStatement instances' in -:31
    Stack trace:
    #0 [internal function]: PDOStatement->__sleep()
    #1 -(31): serialize(Array)
    #2 -(190): __phpunit_run_isolated_test()
    #3 {main}
      thrown in - on line 31
    

    Something with a doctrine/pdo went wrong in one of your tests, but since serialize does not work on pdo-classes, it prints out only this message.
    If you want doctrines/PDOs message, run only this one test with process isolation turned off and you will get the error-message.
    Really bad to debug on a continous-integration-system if it fails only there :)

    ]]>
    Sun, 03 Oct 2010 21:35:47 +0000
    <![CDATA[Stopping symfony from screwing up uploaded files names - PART 2]]> http://www.robo47.net/blog/206-Stopping-symfony-from-screwing-up-uploaded-files-names-PART-2 http://www.robo47.net/blog/206-Stopping-symfony-from-screwing-up-uploaded-files-names-PART-2 After using my last solution for some time i came up with some improvements. Allowing all chars can be a security and usability-problem, so I switched to use some methods Doctrine uses for its slugs now.

    Currently I am still looking for a solution to extend it with some utf-8 support for japanese chars, so they won't be removed. The current solution won't work, it seems that there is no easy way to exclude all kanji from beeing replaced with regex and still replacing umlaute and chars with accents.

    class myValidatedFile extends sfValidatedFile
    {
    
      /**
       * Generates a non random filename
       *
       * @return string A non random name to represent the current file
       */
      public function generateFilename()
      {
        $ext = strtolower($this->getExtension($this->getOriginalExtension()));
        $name = self::urlize(substr($this->getOriginalName(), 0, - strlen($ext)));
    
        $filename = self::urlize($name . $ext);
    
        $i = 1;
        while (file_exists($this->getPath() . '/' . $filename))
        {
          $filename = self::urlize($name . '-' . $i . $ext);
          $i++;
        }
        return $filename;
      }
    
      /**
       * based on Doctrine_Inflector::urlize (without strtolower)
       */
      public function urlize($text)
      {
        // Remove all non url friendly characters with the unaccent function
        $text = Doctrine_Inflector::unaccent($text);
    
        // Remove all none word characters
        $text = preg_replace('/[^\w\.\_\-]/', ' ', $text);
        $text = str_replace(' ', '-', $text);
    
        return trim($text, '-');
      }
    
    }
    
    ]]>
    Thu, 30 Sep 2010 19:17:32 +0000
    <![CDATA[Fast hack for not linking empty columns with the href as linktext in a symfony admin generator list]]> http://www.robo47.net/blog/205-Fast-hack-for-not-linking-empty-columns-with-the-href-as-linktext-in-a-symfony-admin-generator-list http://www.robo47.net/blog/205-Fast-hack-for-not-linking-empty-columns-with-the-href-as-linktext-in-a-symfony-admin-generator-list Using = to make a column linked to the edit-page of an entry in the symfony-admin generator.yml is ugly if an entry's column is empty, because the link_to()-helper will then use the href of the link as link text. If prefere just an empty column.

    This small hack requires replacing the default sfDoctrineGenerator with your own implementation and overwriting the renderField-method:

    class myDoctrineGenerator extends sfDoctrineGenerator
    {
      /**
       * Returns HTML code for a field.
       *
       * @param sfModelGeneratorConfigurationField $field The field
       *
       * @return string HTML code
       */
      public function renderField($field)
      {
        $html = $this->getColumnGetter($field->getName(), true);
    
        if ($renderer = $field->getRenderer())
        {
          $html = sprintf("$html ? call_user_func_array(%s, array_merge(array(%s), %s)) : ' '", $this->asPhp($renderer), $html, $this->asPhp($field->getRendererArguments()));
        }
        else if ($field->isComponent())
        {
          return sprintf("get_component('%s', '%s', array('type' => 'list', '%s' => \$%s))", $this->getModuleName(), $field->getName(), $this->getSingularName(), $this->getSingularName());
        }
        else if ($field->isPartial())
        {
          return sprintf("get_partial('%s/%s', array('type' => 'list', '%s' => \$%s))", $this->getModuleName(), $field->getName(), $this->getSingularName(), $this->getSingularName());
        }
        else if ('Date' == $field->getType())
        {
          $html = sprintf("false !== strtotime($html) ? format_date(%s, \"%s\") : ' '", $html, $field->getConfig('date_format', 'f'));
        }
        else if ('Boolean' == $field->getType())
        {
          $html = sprintf("get_partial('%s/list_field_boolean', array('value' => %s))", $this->getModuleName(), $html);
        }
    
        if ($field->isLink())
        {
          // ugly/simple hack to only print a non printing space if the value is empty
          $html = sprintf("''; \$linktitle = $html; if (!empty(\$linktitle)) { echo link_to(%s, '%s', \$%s); } else { echo ' '; }", $html, $this->getUrlForAction('edit'), $this->getSingularName());
        }
    
        return $html;
      }
    }
    

    and you need to change your generator.yml to use the new generator:

    generator:
      class: myDoctrineGenerator
    
    ]]>
    Thu, 30 Sep 2010 18:17:09 +0000
    <![CDATA[Stopping symfony from screwing up uploaded files names]]> http://www.robo47.net/blog/204-Stopping-symfony-from-screwing-up-uploaded-files-names http://www.robo47.net/blog/204-Stopping-symfony-from-screwing-up-uploaded-files-names Symfonys behaviour to rename uploaded files with a sha1-hash of the original filename and a randomnumber between 11111 and 99999 [why those numbers ?], ( done in sfValidatedFile::generateFilename ) is pretty ugly. No more seo-friendly filenames, filenames where you can actually have a clue about what's behind an url or how the creator supposed it to be named.

    Why symfony has, under some circumstances to change the filesname is pretty easy to understand. Uploading a file with the same name should not throw an error or overwrite the other file, so changing it has to be done in some way under some circumstances. So overwriting the method and returning just the original name would not really help.
    What we need is a way to change the name for files, but ONLY if there already exists a file with that name.

    The easiest way that came to my mind was, if the file already exists, appending something. Since collusions can happen more than once, appending just a fixed value won't help. Since i don't really like adding some random number and hoping no collusion happens later, I wanted an incrementing number to be appended to the filename.
    Spoken in code it can look like this:

    class myValidatedFile extends sfValidatedFile
    {
    
      /**
       * Generates a non-random-filename
       *
       * @return string A non-random name to represent the current file
       */
      public function generateFilename()
      {
        $filename = $this->getOriginalName();
    
        $ext = $this->getExtension($this->getOriginalExtension());
        $name = substr($this->getOriginalName(), 0, - strlen($ext));
        $i = 1;
        while(file_exists($this->getPath() . '/' .  $filename)) {
          $filename = $name . '-' . $i . $ext;
          $i++;
        }
        return $filename;
      }
    }
    

    It checks if the file already exists if not, it tries to append "-" followed by an incrementing number, it checks again, increments the number, checks again, until it finds a free number. No random number, an incrementing number.
    Now the sfValidatorFile has to use this class instead of sfValidatedFile, that can be done by passing its name in on creation of the sfValidatorFile:

    public function configure()
    {
      $this->widgetSchema['image'] = new sfWidgetFormInputFileEditable(array(
        'label' => 'Field Name',
        'file_src' => '/uploads/images/'.$this->getObject()->getImage(),
        'is_image' => true,
        'edit_mode' => !$this->isNew(),
        'template' => '%file% %input% %delete% %delete_label%'
      ));
      
      $this->validatorSchema['image'] = new sfValidatorFile(array(
        'required'   => false,
        'mime_types' => 'web_images',
        'path'       => sfConfig::get('sf_upload_dir').'/images',
        'validated_file_class' => 'myValidatedFile',
      ));
      
      // delete checkbox
      $this->validatorSchema['image_delete'] = new sfValidatorPass();
    }
    
    ]]>
    Wed, 18 Aug 2010 22:05:09 +0000
    <![CDATA[JQuery-based Auto-submit for checkbox and select-filters in the symfony admin generator backend]]> http://www.robo47.net/blog/203-JQuery-based-Auto-submit-for-checkbox-and-select-filters-in-the-symfony-admin-generator-backend http://www.robo47.net/blog/203-JQuery-based-Auto-submit-for-checkbox-and-select-filters-in-the-symfony-admin-generator-backend Nice and simple usability-boost for the filtering in the backend if you use it often and don't want to press the filter-button each time.
    But it too can be annoying if you often filter based on date-fields and don't have a datepicker, because selecting a range is 6 changes and each time it get's submitted.

    <script type="text/javascript">
      $(document).ready(function() {
        $('.sf_admin_filter select, .sf_admin_filter input[type="checkbox"]').attr('onchange', 'this.form.submit()');
      });
    </script>
    
    ]]>
    Sun, 15 Aug 2010 12:51:13 +0000
    <![CDATA[Automatic deletion of thumbnails created by sfImageTransformExtraPlugin when deleting the original image in a symfony admin generator based backend]]> http://www.robo47.net/blog/202-Automatic-deletion-of-thumbnails-created-by-sfImageTransformExtraPlugin-when-deleting-the-original-image-in-a-symfony-admin-generator-based-backend http://www.robo47.net/blog/202-Automatic-deletion-of-thumbnails-created-by-sfImageTransformExtraPlugin-when-deleting-the-original-image-in-a-symfony-admin-generator-based-backend Symfony offers a fast and easy way for setting up admin backends including support for image uploads. You only need to change the fields widget and validator in the Form Class and symfony handles the rest:

    /lib/Form/doctrine/%Tablename%Form.class.php
      public function configure()
      {
        $this->widgetSchema['image'] = new sfWidgetFormInputFileEditable(array(
          'label' => 'Field Name',
          'file_src' => '/uploads/images/'.$this->getObject()->getImage(),
          'is_image' => true,
          'edit_mode' => !$this->isNew(),
          'template' => '%file% %input% %delete% %delete_label%'
        ));
        
        $this->validatorSchema['image'] = new sfValidatorFile(array(
          'required'   => false,
          'mime_types' => 'web_images',
          'path'       => sfConfig::get('sf_upload_dir').'/images',
        ));
        
        // delete checkbox
        $this->validatorSchema['image_delete'] = new sfValidatorPass();
      }
    

    Combining that with the power of sfImageTransformExtraPlugin to create thumbnails of images based on profiles is really great:

    • No need to handle the thumbnail generation for yourself.
    • Easy automatic creation on first usage
    • No need for extra batch script needed if you ever choose to change the thumbnail sizes.

    Big thx to Stuart Lowes for sfImageTransformPlugin and Christian Schäfer for sfImageTransformExtraPlugin.

    The creation of the thumbnails is taken care of now, but what about deleting thumbnails if their base image is deleted ? We need to handle that ourselfs.

    My first thought was to set up a cronjob to handle that task.
    It could scan the upload directory and delete all images from the thumbnail directory which don't have a corresponding one in the upload directory. A perfect task for sfFinder and sfFilesystem. But that would mean each project would have to setup a cronjob and there would always be a delay until the files are really deleted. That was not what I wanted.

    Since I am new to symfony and its admin generation, I had to understand what happens behind the curtain first. What came up with is an easy solution extending the BaseFormDoctrine class by overwriting the removeFile() method.

    /lib/form/doctrine/BaseFormDoctrine.class.php
    abstract class BaseFormDoctrine extends sfFormDoctrine
    {
      public function setup()
      {
      }
      
      /**
       * Removes the uploaded image AND all thumbnails created by sfImageTransformExtraPlugin in %sf_root_dir%/web/thumbnails/
       */
      protected function removeFile($field)
      {
        if (!$this->validatorSchema[$field] instanceof sfValidatorFile)
        {
          throw new LogicException(sprintf('You cannot remove the current file for field "%s" as the field is not a file.', $field));
        }
    
        $directory = $this->validatorSchema[$field]->getOption('path');
        if ($directory && is_file($file = $directory.'/'.$this->getObject()->$field))
        {
          $finder = sfFinder::type('file');
          /* @var $finder sfFinder */
          $finder->name($this->getObject()->$field);
          $files = $finder->in(sfConfig::get('sf_root_dir') . '/web/thumbnails/');
          $filesystem = new sfFilesystem();
          $filesystem->remove($files);
        }
    
        parent::removeFile($field);
      }
    }
    

    It usessfFinder, which searches all files with the same filename as the file in the thumbnails directory and then deletes them using sfFileSystem->remove().
    Pretty simple.

    It's always great to see what you can achieve in symfony with only some minor changes.

    ]]>
    Sun, 15 Aug 2010 12:34:13 +0000
    <![CDATA[Running phpunit in a loop and let it inform you when you broke something]]> http://www.robo47.net/blog/201-Running-phpunit-in-a-loop-and-let-it-inform-you-when-you-broke-something http://www.robo47.net/blog/201-Running-phpunit-in-a-loop-and-let-it-inform-you-when-you-broke-something When refactoring code or tests I like working until I break something. When my phpunit tests switch from green to red, I want to know it immediately without restarting the tests manually each time I change something, because that's annoying and a waste of time.
    So after thinking about running the test-suite after each save in Netbeans, which I didn't find a way to do it and for unittests which run longer than some seconds, it wouldn't probably be such a good solution if after saving some changes the testsuite will take minutes to run. So I came up with another simple solution:

    while true; do phpunit; if [ $? -ne 0 ]; then break; else sleep 5; fi; done;
    

    This one-liner runs phpunit in a loop, displays the output eatch time, sleeps for 5 seconds after finishing the run and stopfs if something breaks, so you'll see the last report.
    I tweeted that already some time ago, but it was annoying to be distracted by the continous output.
    So I had to make it less distracting. Now it outputs the lastest phpunit-output only if it fails and thus is much less distractive.

    while true; do FOO=`/usr/bin/phpunit`; if [ $? -ne 0 ]; then echo $FOO; break; else sleep 5; fi; done;
    

    But, it wasn't perfect yet. Since I sometimes didn't notice, when tests failed, I needed some harder way to notify me whie I am focused on coding.
    So I came up with this version, which plays a sound (from openoffice) via vlc if a test fails:

    while true; do FOO=`/usr/bin/phpunit`; if [ $? -ne 0 ]; then echo $FOO; vlc --play-and-exit "/usr/lib/openoffice/basis3.2/share/gallery/sounds/ups.wav" 2> /dev/null & ; break; else sleep 5; fi; done;
    

    And if you prefer a popup instead of a hell output and sound you can use xmessage for that:

    while true; do FOO=`/usr/bin/phpunit`; if [ $? -ne 0 ]; then xmessage -center "$FOO" &; break; else sleep 5; fi; done;
    

    This will look somehow like that:

    error popup from phpunit via xmessage ]]>
    Sun, 15 Aug 2010 10:20:42 +0000
    <![CDATA[Truecrypt 7.0 Linux AES-NI Benchmark with i7-620M on Dell Latitude E6510]]> http://www.robo47.net/blog/200-Truecrypt-7.0-Linux-AES-NI-Benchmark-with-i7-620M-on-Dell-Latitude-E6510 http://www.robo47.net/blog/200-Truecrypt-7.0-Linux-AES-NI-Benchmark-with-i7-620M-on-Dell-Latitude-E6510 The new Truecrypt 7.0 release is almost 7 times faster compared to 6.0 on my i7-620M with AES-NI. It is some hundred mb/s faster now than dmcrypt (which runs my system-encryption on Debian Squeeze), but that is expected since truecrypt makes use of multiple cores AND aes-ni and dmcrypt only supports 1 thread per mounted device, so unless you create a RAID consisting of multiple dmcrypt-devices, you can only use 1 core.

    Another nice thing, they ported the included benchmark-tool from windows to linux. In 6.3 there was no benchmarking when running under Linux, but now it is supported.

    Truecrypt achieves between 1600 and 1700 mb/s. I ran the benchmark for 1mb, 5mb, 10mb, 50mb, 100mb and 1024mb, skipped 200mb and 500mb, because I was tooo lazy :)

    Here are some images:

    Truecrypt 7.0 Benchmark i7-620M AES-NI 1mb
    Truecrypt 7.0 Benchmark i7-620M AES-NI 5mb
    Truecrypt 7.0 Benchmark i7-620M AES-NI 10mb
    Truecrypt 7.0 Benchmark i7-620M AES-NI 50mb
    Truecrypt 7.0 Benchmark i7-620M AES-NI 100mb
    Truecrypt 7.0 Benchmark i7-620M AES-NI 1024mb

    To compare to before with Truecrypt 6 under Windows already with multi-core-support but without AES-NI support on the same Intel i7-620M:

    Truecrypt 6 Windows Benchmark i7-620M without AES-NI 100mb

    Let's see when dmcrypt gets support for multiple core per device.

    ]]>
    Tue, 20 Jul 2010 19:49:54 +0000
    <![CDATA[Using Chromium (Googles Opensource Chrome) under Debian Squeeze from an Ubuntu PPA - daily]]> http://www.robo47.net/blog/199-Using-Chromium-Googles-Opensource-Chrome-under-Debian-Squeeze-from-an-Ubuntu-PPA-daily http://www.robo47.net/blog/199-Using-Chromium-Googles-Opensource-Chrome-under-Debian-Squeeze-from-an-Ubuntu-PPA-daily If you are looking for an easy way of using an up to date version of Chromium (the free open-source version of Goole's Chrome browser) on Debian Squeeze you should try one of the Ubuntu Chromium Projects PPA Repositories, they have a beta based on the version used for the current Chrome Linux-Builds and and a daily-trunk based one.

    Important thing is that you use the karmic-repository, lucid didn't work for me.

    Daily
    deb http://ppa.launchpad.net/chromium-daily/ppa/ubuntu karmic main 
    deb-src http://ppa.launchpad.net/chromium-daily/ppa/ubuntu karmic main
    
    Beta
    deb http://ppa.launchpad.net/chromium-daily/beta/ubuntu karmic main
    deb-src http://ppa.launchpad.net/chromium-daily/beta/ubuntu karmic main
    

    The package is named chromium-browser

    ]]>
    Mon, 24 May 2010 20:00:11 +0000
    <![CDATA[Intel AES-NI dmcrypt benchmark with i7-620M on Debian Squeeze]]> http://www.robo47.net/blog/198-Intel-AES-NI-dmcrypt-benchmark-with-i7-620M-on-Debian-Squeeze http://www.robo47.net/blog/198-Intel-AES-NI-dmcrypt-benchmark-with-i7-620M-on-Debian-Squeeze A benchmarks showing how awesome fast dmcrypt is with AES-NI.

    A benchmarks showing how awesome fast dmcrypt is with AES-NI. AES-NI was the main-cause I choose an i7-620M dualcore instead of an i7-720QM quadcore, since there are no quadcores with aes-ni yet, the only more than 2-core cpus with AES-NI are 6-core XEONs.

    1021944+0 records in
    1021944+0 records out
    523235328 bytes (523 MB) copied, 0.915509 s, 572 MB/s
    1021944+0 records in
    1021944+0 records out
    523235328 bytes (523 MB) copied, 0.917671 s, 570 MB/s
    1021944+0 records in
    1021944+0 records out
    523235328 bytes (523 MB) copied, 0.90225 s, 580 MB/s
    1021944+0 records in
    1021944+0 records out
    523235328 bytes (523 MB) copied, 0.920597 s, 568 MB/s
    1021944+0 records in
    1021944+0 records out
    523235328 bytes (523 MB) copied, 0.915225 s, 572 MB/s
    1021944+0 records in
    1021944+0 records out
    523235328 bytes (523 MB) copied, 0.915535 s, 572 MB/s
    1021944+0 records in
    1021944+0 records out
    523235328 bytes (523 MB) copied, 0.912177 s, 574 MB/s
    1021944+0 records in
    1021944+0 records out
    523235328 bytes (523 MB) copied, 0.919594 s, 569 MB/s
    1021944+0 records in
    1021944+0 records out
    523235328 bytes (523 MB) copied, 0.900472 s, 581 MB/s
    1021944+0 records in
    1021944+0 records out
    523235328 bytes (523 MB) copied, 0.919543 s, 569 MB/s
    

    About 570mb/s ... should be enough for a SSD and having some power for work :)

    Run tests are based on the test-setup described here in the german debianforum-wiki:

    Debian Forum Wiki - BenchmarkFestplattenverschlüsselung

    using a ramdrive to copy a 500mb file.

    With deactivated AES-NI I only have tested on an old Kubuntu 8.04 with 32bit kernel, because running a completely encrypted system I can't unload aes-ni while running, and the newer live-cd's (grml64, ubuntu 10.04) I had lying around didn't boot on my new notebook. So the comparision isn't really fair, the values I got on my first test-installation of squeeze without aes-ni (but didn't save before reinstalling) where between 95 and 120mb/s with a running gnome and stuff.

    1021944+0 Datensätze ein
    1021944+0 Datensätze aus
    523235328 Bytes (523 MB) kopiert, 5,21284 s, 100 MB/s
    1021944+0 Datensätze ein
    1021944+0 Datensätze aus
    523235328 Bytes (523 MB) kopiert, 5,26647 s, 99,4 MB/s
    1021944+0 Datensätze ein
    1021944+0 Datensätze aus
    523235328 Bytes (523 MB) kopiert, 5,37728 s, 97,3 MB/s
    1021944+0 Datensätze ein
    1021944+0 Datensätze aus
    523235328 Bytes (523 MB) kopiert, 5,26647 s, 99,4 MB/s
    1021944+0 Datensätze ein
    1021944+0 Datensätze aus
    523235328 Bytes (523 MB) kopiert, 5,23903 s, 99,9 MB/s
    1021944+0 Datensätze ein
    1021944+0 Datensätze aus
    523235328 Bytes (523 MB) kopiert, 5,22523 s, 100 MB/s
    1021944+0 Datensätze ein
    1021944+0 Datensätze aus
    523235328 Bytes (523 MB) kopiert, 5,10732 s, 102 MB/s
    1021944+0 Datensätze ein
    1021944+0 Datensätze aus
    523235328 Bytes (523 MB) kopiert, 5,12601 s, 102 MB/s
    1021944+0 Datensätze ein
    1021944+0 Datensätze aus
    523235328 Bytes (523 MB) kopiert, 4,94373 s, 106 MB/s
    1021944+0 Datensätze ein
    1021944+0 Datensätze aus
    523235328 Bytes (523 MB) kopiert, 5,13347 s, 102 MB/s
    

    Running Kubuntu 8.04 (Linux ubuntu 2.6.24-16-generic #1 SMP Thu Apr 10 13:23:42 UTC 2008 i686 GNU/Linux)

    Compared with truecrypt which is able to use multiple processors/cores on one drive (dmcrypt only allows 1 thread per drive) but does not support AES-NI (yet?) dmcrypt with AES-NI is still faster.

    Running the Truecrypt-Benchmarks under Windows 7 I got about 250mb/s for AES
    More on the Screenshot I took:

    Truecrypt i7-620M Benchmark Windows 7

    In practice i really don't feel the encryption, not like on my old p4 which could only get about 44mb/s and where copying data from disk to disk was really limited by the cpu.

    On the Wiki of the german debianforum you can find more benchmarks of other cpus and systems, some featuring AES-NI too:
    Benchmark Festplattenverschlüsselung

    ]]>
    Sun, 23 May 2010 00:05:45 +0000
    <![CDATA[Setting up Linux (Debian Squeeze) on a Dell Latitude E6510 with Nvida Quadro NVS 3100M and 1920x1080 Display]]> http://www.robo47.net/blog/197-Setting-up-Linux-Debian-Squeeze-on-a-Dell-Latitude-E6510-with-Nvida-Quadro-NVS-3100M-and-1920x1080-Display http://www.robo47.net/blog/197-Setting-up-Linux-Debian-Squeeze-on-a-Dell-Latitude-E6510-with-Nvida-Quadro-NVS-3100M-and-1920x1080-Display New notebooks are sometimes a little pain in the ass to get working with Linux. After more than 4 weeks, my new notebook finally arrived last monday. Monday this week I tried installing Debian Squeeze (still is testing) on my new Latitude and after 2 hours formatting the drive with dmcrypt, failed with a not starting X, not finding a screen. I didn't have the time to check it out because I needed a running System with OpenGL and Virtualbox to run my old Windows XP-Image using VisualStudio with the setup OpenGL-Stuff for university, so I went with Windows 7 for the first 3 days.

    My Latitude E6510 specs:

    • Intel i7-620M 2.66 GHZ Dualcore with AES-NI and Turbo Mode (biggest dualcore with AES-NI offered with the new Latitude-series)
    • 4GB RAM (2 * 2GB) (8GB would have been 250€(+taxes) more)
    • 250GB @7200RPM HDD (had 120GB in my old one, but never used it, since all big data normally goes on to external drive or the raid in my server)
    • 15.6" @ 1920x1080 (After working over 4 1/2 year with 1024*768 it was time to get big)
    • Nvidia Quadro NVS 3100M (well no player-card, but at least it seems some old games run on it)
    • Intel Centrino Advanced-N 6200 Wifi

    On Thursday I completed the OpenGL-Stuff and had time to pay some attention to the problem. I tought why not try an up-to-date Live-CD (Ubuntu 10.04 Live-CD) first, to see if that at least works. I downloaded the new Ubuntu 10.04-live-cd and the problem was worse, not even an error-message that it couldn't start X, only a black screen after starting. Then installed Debian Squeeze again from the latest DVD and started investigating what the problem was, it seemed to have problems with not finding a display.

    First I added the following lines to /etc/apt/sources.list:

    deb http://ftp.debian.org/debian/ squeeze main non-free contrib
    deb-src http://ftp.debian.org/debian/ squeeze main non-free contrib
    

    and comment out the cd-rom line and install nvidia-xconfig:

    apt-get update
    apt-get install nvidia-xconfig
    

    Then I created a basic xorg-conf with the nvidia-tool:

    nvidia-xconfig
    

    and edited it to use vesa instead of nvidia until we have setup the property nvidia driver for the Nvidia Quadro NVS 3100M and add the 1920x1080 resolution for the display to work.

    Thats what the device-section should look like:

    /etc/X11/XF86Config
    Section "Device"
    Identifier     "Device0"
    Driver         "vesa"
    VendorName     "NVIDIA Corporation"
    EndSection
    

    and that would be the screen-section

    Section "Screen"
    Identifier     "Screen0"
    Device         "Device0"
    Monitor        "Monitor0"
    DefaultDepth    24
    SubSection     "Display"
    Depth       24
    Modes        "1920x1080@60"
    EndSubSection
    EndSection
    

    with the added 1920x1080 resolution.

    Now we can restart the system and at least work with a X-Windows, decent resolution and start installing the nvidia-driver.

    I used the entry in the debian-wiki for setting up the nvidia-driver: Debian Wiki: Nvidia Graphics Card which was really easy, fast and worked. In short:

    apt-get install module-assistant nvidia-kernel-common
    m-a auto-install nvidia-kernel-source
    

    that will install some additional packages like the right gcc used by the kernel (currently 4.3) and stuff.
    Then I changed from vesa back to nvidia in the XF86Config.

    /etc/X11/XF86Config
    Section "Device"
    Identifier     "Device0"
    Driver         "nvidia"
    VendorName     "NVIDIA Corporation"
    EndSection
    

    After rebooting I was able to use Nvidias Tool ( apt-get install nvidia-settings if not already installed while setting up the driver and open via System -> Administration -> NVIDIA X Server Settings) to setup my dual-screen-setup (21" CRT @ 1600 * 1200 above my notebook) like wanted.

    Next thing was the wireless lan, which wasn't supported out of the box on debian since it needs a binary firmware-package which are non-free and not default in debian. There for the already added non-free in /etc/apt/sources.list is necessary. The right package for the Intel Corporation Centrino Advanced-N 6200 is firmware-iwlwifi.

        apt-get install firmware-iwlwifi
    

    Now a

    ifconfig wlan0 up

    should load the kernel-module and activate the card.

    If you get a

    SIOCSIFFLAGS: Unknown error 132

    probably you turned of your wifi with the switch on the right side of you notebook next to the firewire ... took me about an houer to realize it was turned of and the wifi-led not on.

    Now it was time for updating to the latestet packages, since there has a lot changed since the last debian squeeze beta-cd/dvd over 400 packages or me and about 900mb to download ... took some time with 240kb/s.

    apt-get update && apt-get upgrade && apt-get dist-upgrade

    That's it for the start, some minor things I did afterwards:

    • Setting a DPI-value for my monitors (140+ was ugly big, now going with 90 for both displays)

    Infos on how to set DPI:

    Some things still to be done:

    • Find out how to turn off the touchpad but not the stick. Because I hate touchpads and to often accidently push it and in my opionion sticks are much superior :)
    • Find out if the mysterious mouse-movement/clicking happening sometimes while writing is caused by touchpad, stick or something else
    • Turning off the annoying beep (login / console) of the speaker (seems like blacklisting pcspkr doesn't do the trick alone)
    • Fixing my conkyrc to work with i2c instead of hwmon
    • Getting used to the keyboard, since some things changed a lot from my thinkpad (ESC-key on same line with F-keys, a windows-key, ctrl and FN exchanged, sound-control on right side of keyboard, ... )
    ]]>
    Sat, 22 May 2010 16:00:11 +0000
    <![CDATA[Samsung R530 - So many crap-games/apps preinstalled and Phoenix Failsafe - without an uninstaller ...]]> http://www.robo47.net/blog/196-Samsung-R530-So-many-crap-games-apps-preinstalled-and-Phoenix-Failsafe-without-an-uninstaller-... http://www.robo47.net/blog/196-Samsung-R530-So-many-crap-games-apps-preinstalled-and-Phoenix-Failsafe-without-an-uninstaller-... Today I helped somebody buy a cheap notebook as a replacement for a really old PC.
    In the end we went home with a Samsung R530 and I immediately started configuring it.

    After the installation I was looking on a lot of crap on the desktop and the startmenu.
    I knew most notebooks today come with a lot of preinstalled crap like MS Office, ISP-software (AOL, T-Online, ... ) , virus-scanner, firewalls and more stuff (the same here, 60-day-trial for the new office, a McAfee Security-Solution (virus-scanner, anti-spam and more), ...) but I didnt see THAT coming.
    Lots of games and programs from a company I already forgot the name, which which where usable for some hours but then you would have pay for them. I uninstalled them, even though they were from the same company and only some mb in size, each had it's own uninstaller. But I figured the low price for the notebook is partly due to those programs.
    Then I discovered a popup for the most awesome program ever Phoenix FailSafe ... a product which helps you when your notebook gets stolen. It tracks it, deletes/downloads your data, makes a photo/video of the thief and stuff like that if he goes online or is in reach of any accessable gps-device the notebook can connect to (bluetooth or whatever) . It was a trial too and you would have to pay for it on a monthly base.

    The first thing I did was searching for an uninstaller in the starmenu: nothing.
    Then in Windows software-control: nothing.
    Then I searched on google and found a thread about it. No real solution.

    You can set it up, register, then login on their page, deactivate it and then download an "uninstaller"! Register to uninstall it ? WTF!!!

    According to an entry in the forum, the support said, you can "uninstall" it manually by deleting the directory, the files and the start-menu entry by hand ...


    What kind of crap is that ? A silly way to protect it from beeing deleted by a potential thief ? I would accept it if it would remove it's entry in start-menu and uninstaller after you have choosen to use it. But not even providing one ?
    I will give the manual way a try next week and look if there is more data from this tool in the registry, autostart, services or anywhere else.

    ]]>
    Wed, 03 Mar 2010 22:46:55 +0000
    <![CDATA[Installing HTML Validator Extensions for Firefox / Iceweasel on Debian 6.0 Squeeze]]> http://www.robo47.net/blog/195-Installing-HTML-Validator-Extensions-for-Firefox-Iceweasel-on-Debian-6.0-Squeeze http://www.robo47.net/blog/195-Installing-HTML-Validator-Extensions-for-Firefox-Iceweasel-on-Debian-6.0-Squeeze The HTML Validator-Extension for Firefox doesn't run on Debian Squeeze out of the box, the problem is the missing libstdc++5, but only libstdc++6 is installable from debians default-repositoy.

    Well today I wasted about 12+ hours trying to compile firefox + html validator-extension from scratch, made many mistakes, first I used ff3.5-sources, then I found out that the instructions where not meant for firefox 3.5, but for firefox 2, then some typos on patching source-files that I only foundout about ... 40 minutes later, because building it took between 30 and 60minutes each time ... That's why 12 hours past so fast.

    But finally I found a way to get it running. libstdc++5 wasn't the only problem :)

    Using the solutions provided for other linux distributions, mixing them a bit and doing some testing myself I finally got a solution:

    First thing to do, get the old libstdc++5 for Debian Lenny and install it:
    http://packages.debian.org/lenny/i386/libstdc++5/download
    If this doesn't work for you, extract the deb and the included data.tar.gz and copy the content of the usr/lib-dir into /usr/lib.

    Then install libnspr4-dev and xulrunner-dev via apt-get:

    apt-get install libnspr4-dev xulrunner-dev
    

    (I hope that's all you need since I have lost track what packages I installed today, what i uninstalled later and what I had already installed)

    next thing to do, create 2 symlinks:

    ln -s /usr/lib/xulrunner-1.9.1/libxul.so /usr/lib/libxul.so
    	ln -s /usr/lib/xulrunner-1.9.1/libxpcom.so /usr/lib/libxpcom.so
    

    Now you should be able to install html validator and it should work and not open a tab with the showing you the no tidy lib-page
    FATAL ERROR : The dynamic C library  contained in the extension file could not be found.

    If it still NOT works, check with ldd what other libs might be missing like described on the authors homepage:
    http://users.skynet.be/mgueury/mozilla/faq.html#LINUX
    -> On another type of linux with the problem

    ]]>
    Fri, 26 Feb 2010 23:49:04 +0000
    <![CDATA[How do NOTICES influence php-scripts performance ?]]> http://www.robo47.net/blog/194-How-do-NOTICES-influence-php-scripts-performance http://www.robo47.net/blog/194-How-do-NOTICES-influence-php-scripts-performance Having worked and refactored a of lot ugly code in my life and seen a lot more (often on helping people on php.de) I was wondering how big the impact of NOTICES thrown by php really is.
    Since php allows a lot of really bad code and it is easy to turn off showing them (in most cases it is turned off by default and not logged either) I wanted to test some of those things you see almost every day, usage of uninitialized variables and accessing array keys without setting them into quotes.

    Since this kind of bad code in most cases are only a small part of the complete applications code, it won't have a huge impact on the total performance of a script, but I wanted to know how much the difference for those code-parts would be.

    I made some tests with code which assigns a non-existing variable to another and accesses a non-existing array-key.

    I used the php-binary directly (php -f file.php), suppressed the display of the errors by using error_reporting() and init_set with display_errors to false and log_errors to false because displaying and logging all those errors would have messed up the result.

    Since only one line is hard to test because execution-time is just too short, I put that stuff into a loop to get some bigger values, 10 Million iterations made a good value for me.


    I then ran the code on my notebook (p4 mobile 1.73ghz/2GB RAM debian squeeze php 5.2.12 [32bit OS]) and on my server (i7 920@2.67Ghz/8GB RAM debian lenny php 5.2.12 [64bit OS]).

    Code 1: initialized variable vs uninitialized variable

    <?php
    	error_reporting(E_ALL | E_STRICT);
    	ini_set('display_errors', false);
    	ini_set('log_errors', false);
    	
    	$var = 1;
    	
    	for ($i = 0; $i < 10000000; $i++) {
    	    $foo = $var;
    	}
    

    and

    <?php
    	error_reporting(E_ALL | E_STRICT);
    	ini_set('display_errors', false);
    	ini_set('log_errors', false);
    	
    	for ($i = 0; $i < 10000000; $i++) {
    	    $foo = $var;
    	}
    
    System initialzed variable uninitialized variable  
    Notebook 2.36s 13.23s 5.6 times slower
    Server 0.91s 4.73s 5.2 times slower

    Code 2: array key in quotes vs no quotes

    <?php
    	error_reporting(E_ALL | E_STRICT);
    	ini_set('display_errors', false);
    	ini_set('log_errors', false);
    	
    	$array = array('foo' => 'baa');
    	
    	for ($i = 0; $i < 10000000; $i++) {
    	    $foo = $array['foo'];
    	}
    

    and

    <?php
    	error_reporting(E_ALL | E_STRICT);
    	ini_set('display_errors', false);
    	ini_set('log_errors', false);
    	
    	$array = array('foo' => 'baa');
    	
    	for ($i = 0; $i < 10000000; $i++) {
    	    $foo = $array[foo];
    	}
    
    System array key with quotes array key without quotes  
    Notebook 3.36s 19.7s 5.8 times slower
    Server 1.4s 7.38s 5.2 times slower

    It will propably not boost you application to lightspeed, but it can make it a bit faster and will increase your code-quality.

    Activating php's error-logging made it ... a lot slower, 25 times slower on my notebook [Slow 120GB 5400RPM 2,5" Notebook-Drive) and about 15 times slower on my server [SATA 750GB RAID-1], but that I only tested with less iterations (10000) because it took so long and logfiles got really big.

    So please, use error_reporting(E_ALL | E_STRICT); in your apps and turn display_errors and log_errors on. In production turning off display_errors is okay, but at least keep logging the errors on and check the logs.

    ]]>
    Sun, 21 Feb 2010 23:11:48 +0000
    <![CDATA[Installing Mysql Workbench 5.2.11 from source on Debian 6.0 Squeeze]]> http://www.robo47.net/blog/193-Installing-Mysql-Workbench-5.2.11-from-source-on-Debian-6.0-Squeeze http://www.robo47.net/blog/193-Installing-Mysql-Workbench-5.2.11-from-source-on-Debian-6.0-Squeeze Steps are simple:

    Download the sources ~ 14 MB
    extract it
    install ~ 30mb of packages via apt-get
    run autogen
    make && make install
    start it

    But it may some time, at least on my slow notebook (p4 mobile 1,73 ghz) autogen took 10 minutes, make took about 60 minutes and make install another 4 minutes, but if you ever compiled mysql-server yourself you now ... mysql may take some time :)

    So here in Detail, almost copy & paste-ready:
     

    	wget ftp://ftp.gwdg.de/pub/misc/mysql/Downloads/MySQLGUITools/mysql-workbench-oss-5.2.11.tar.gz
    	tar -xzf mysql-workbench-oss-5.2.11.tar.gz
    	cd mysql-workbench-oss-5.2.11
    	apt-get install libzip-dev libzip1 libxml2-dev libsigc++-2.0-dev libcairomm-1.0-dev libglibmm-2.4-dev libpangomm-1.4-dev libglade2-dev libaudiofile-dev libavahi-client-dev libavahi-common-dev libavahi-glib-dev libbonobo2-dev libdbus-1-dev libesd0-dev libgconf2-dev libgcrypt11-dev libgnomevfs2-dev libgnutls-dev libgpg-error-dev libpopt-dev libselinux1-dev libsepol1-dev libtasn1-3-dev libsqlite3-dev libboost1.40-dev  libmysqlclient-dev libmysqld-dev uuid-dev liblua5.1-0-dev libncurses5-dev libreadline-dev libreadline6-dev libfribidi-dev libgl1-mesa-dev libglc-dev libglc0 libglu1-mesa-dev mesa-common-dev libpcre3-dev libpcrecpp0 python-paramiko
    	./autogen.sh --prefix=/path/where/you/want/to/install/it
    	make
    	make install
    	/path/where/you/did/install/it/bin/mysql-workbench
    

    For those guys who already tried it and got errors for missing packages, a python-error for missing import or a seg-faults, here my list of errors and the packages/action required to install/run:

    No package 'libzip' found

    checking for ZIP... configure: error: Package requirements (libzip) were not met:
    
    No package 'libzip' found
    
    Consider adjusting the PKG_CONFIG_PATH environment variable if you
    installed software in a non-standard prefix.
    
    Alternatively, you may set the environment variables ZIP_CFLAGS
    and ZIP_LIBS to avoid the need to call pkg-config.
    See the pkg-config man page for more details.
    

    libzip dev package is missing install it via apt-get:

    apt-get install libzip-dev libzip1
    

    No package 'libxml-2.0' found

    checking for GLIB... configure: error: Package requirements (glib-2.0 gthread-2.0 gmodule-2.0 libxml-2.0 >= 2.6.2) were not met:
    
    No package 'libxml-2.0' found
    
    Consider adjusting the PKG_CONFIG_PATH environment variable if you
    installed software in a non-standard prefix.
    
    Alternatively, you may set the environment variables GLIB_CFLAGS
    and GLIB_LIBS to avoid the need to call pkg-config.
    See the pkg-config man page for more details.
    

    libxml dev package is missing install it via apt-get:

    apt-get install libxml2-dev
    

    No package 'sigc++-2.0' found

    checking for SIGC... configure: error: Package requirements (sigc++-2.0) were not met:
    
    No package 'sigc++-2.0' found
    
    Consider adjusting the PKG_CONFIG_PATH environment variable if you
    installed software in a non-standard prefix.
    
    Alternatively, you may set the environment variables SIGC_CFLAGS
    and SIGC_LIBS to avoid the need to call pkg-config.
    See the pkg-config man page for more details.
    

    sigc++2.0 dev package is missing install it via apt-get:

    apt-get install libsigc++-2.0-dev
    

    No package 'libglade-2.0' found / No package 'gtkmm-2.4' found/h3>

    checking for GNOME... configure: error: Package requirements (libglade-2.0 gtkmm-2.4) were not met:
    
    No package 'libglade-2.0' found
    No package 'gtkmm-2.4' found
    
    Consider adjusting the PKG_CONFIG_PATH environment variable if you
    installed software in a non-standard prefix.
    
    Alternatively, you may set the environment variables GNOME_CFLAGS
    and GNOME_LIBS to avoid the need to call pkg-config.
    See the pkg-config man page for more details.
    

    libglade2 dev and libgtkmm 2 dev packages are missing install them via apt-get:

    apt-get install libcairomm-1.0-dev libglibmm-2.4-dev libpangomm-1.4-dev libglade2-dev libaudiofile-dev libavahi-client-dev libavahi-common-dev libavahi-glib-dev libbonobo2-dev libdbus-1-dev libesd0-dev libgconf2-dev libgcrypt11-dev libgnomevfs2-dev libgnutls-dev libgpg-error-dev libpopt-dev libselinux1-dev libsepol1-dev libtasn1-3-dev
    

    No package 'libgnome-2.0' found

    checking for LIBGNOME... configure: error: Package requirements (libgnome-2.0) were not met:
    
    No package 'libgnome-2.0' found
    
    Consider adjusting the PKG_CONFIG_PATH environment variable if you
    installed software in a non-standard prefix.
    
    Alternatively, you may set the environment variables LIBGNOME_CFLAGS
    and LIBGNOME_LIBS to avoid the need to call pkg-config.
    See the pkg-config man page for more details.
    

    libgnome dev package is missing install it via apt-get:

    apt-get install libgnome2-dev
    

    No package 'sqlite3' found

    checking for SQLITE3... configure: error: Package requirements (sqlite3) were not met:
    
    No package 'sqlite3' found
    
    Consider adjusting the PKG_CONFIG_PATH environment variable if you
    installed software in a non-standard prefix.
    
    Alternatively, you may set the environment variables SQLITE3_CFLAGS
    and SQLITE3_LIBS to avoid the need to call pkg-config.
    See the pkg-config man page for more details.
    

    libsql3 dev package is missing install it via apt-get:

    apt-get install libsqlite3-dev
    

    configure: error: "BOOST library is missing"

    checking for boost/foreach.hpp... no
    configure: error: "BOOST library is missing"
    

    libboost dev package is missing install it via apt-get:

    apt-get install libboost1.40-dev
    

    configure: error: Could not find mysql_config script. Make sure the mysql client libraries are installed

    checking for mysql headers and libraries... ./configure: line 16478: mysql_config: command not found
    ./configure: line 16479: mysql_config: command not found
    configure: error: Could not find mysql_config script. Make sure the mysql client libraries are installed
    

    libmysqlclient and libmysqld dev packages are missing install them apt-get:

    apt-get install libmysqlclient-dev libmysqld-dev
    

    No package 'uuid' found

    checking for UUID... configure: error: Package requirements (uuid) were not met:
    
    No package 'uuid' found
    
    Consider adjusting the PKG_CONFIG_PATH environment variable if you
    installed software in a non-standard prefix.
    
    Alternatively, you may set the environment variables UUID_CFLAGS
    and UUID_LIBS to avoid the need to call pkg-config.
    See the pkg-config man page for more details.
    

    uuid dev package is missing install it via apt-get:

    apt-get install uuid-dev
    

    No package 'lua' found

    checking for LUA... configure: error: Package requirements (lua >= 5.1) were not met:
    
    No package 'lua' found
    
    Consider adjusting the PKG_CONFIG_PATH environment variable if you
    installed software in a non-standard prefix.
    
    Alternatively, you may set the environment variables LUA_CFLAGS
    and LUA_LIBS to avoid the need to call pkg-config.
    See the pkg-config man page for more details.
    

    liblua5.1 dev package is missing install it via apt-get:

    apt-get install liblua5.1-0-dev libncurses5-dev libreadline-dev libreadline6-dev
    

    configure: error: OpenGL headers not found

    checking for GL/gl.h... no
    configure: error: OpenGL headers not found
    

    opengl dev packages are missing install them via apt-get:

    apt-get install liblua5.1-0-dev libncurses5-dev libreadline-dev libreadline6-dev
    

    configure: error: Could not find pcre-config script. Make sure the pcre libraries are installed

    checking for pcre-config... no
    configure: error: Could not find pcre-config script. Make sure the pcre libraries are installed
    

    libprce dev and libpcrecpp package are missing install them via apt-get:

    apt-get install libpcre3-dev libpcrecpp0
    

    ImportError: No module named paramiko

    Traceback (most recent call last):
      File "/opt/wb/share/mysql-workbench/sshtunnel.py", line 10, in <module>
        import paramiko
    ImportError: No module named paramiko
    terminate called after throwing an instance of 'std::runtime_error'
      what():  Error starting tunnel manager: 
    Aborted</module>

    python-paramiko is missing install it via apt-get:

    
    

    segmentation fault ./mysql-workbench-bin

    [2]    20800 segmentation fault  ./mysql-workbench-bin
    

    Dont start mysql-workbench-bin, use mysql-workbench instead

    ]]>
    Sat, 09 Jan 2010 02:25:45 +0000
    <![CDATA[Caching Libraries and Opcode-Caches in php - An Overview]]> http://www.robo47.net/blog/192-Caching-Libraries-and-Opcode-Caches-in-php-An-Overview http://www.robo47.net/blog/192-Caching-Libraries-and-Opcode-Caches-in-php-An-Overview Caching data is an important part in todays Web-Application, it can boost the performance, save time and resources or can be necessary because of limited or slow calls to external apis and services.

    Things you can cache are for example database-querys, api-calls, generated data (html, images, .... ), return-values of methods and a lot more things.

    Here is an overview of classes you can use to cache all kinds of data. From PEAR over the Symfony Framework to the Zend Framework. With slow file or database-based backends and fast memory-based backends like APC, Memcached, eAccelerator or Redis. After the caching-classes is a list with opcode-caches, because some of them offer memory-based cache-backends.
     

    PHP-Caching Classes

     

    Class Features Frontends / Backends Version / License / PHP-Version Tutorials / Snippets
    PEAR Cache
    • Different Frontends
    • Different Backends
    Frontends:
    • Standard
    • Application
    • Output
    • OutputCompression
    • HTTP-Request
    • Function
    • Graphics
    Backends:
    1.5.5 (2008-10-07)

    PHP License 2.02

    PHP 4.3.0
    PEAR Cache_Lite

    Documentation
    • Different Frontends
    Frontends:
    • Standard
    • File
    • Function
    • Output
    Backends:
    • File
    1.7.8 (2009-07-07)

    LGPL

    PHP 4.0.0

    sfCache

    sfCache-API-Description

    • Different Frontends
    • Different Backends
    via Plugin: Tagging
    Frontends:
    • Standard
    • Function
    Backends:
    via Plugin: Redis, Zend Server (Disk + SHM)
    1.4 (2009-12-01) Symfony Framework
    MIT License
    PHP 5.2.4
     
    Zend_Cache

    Documentation
    • Tagging (some backends)
    • Different Frontends
    • Different Backends
    Frontends:
    • Standard
    • Class
    • File
    • Function
    • Output
    • Page
    Backends:

    Additional Backends (not Part of the Zend Framework):
    Redis
    eAccelerator
    Zend_Registry
    1.9.6 (2009-11-23)

    New BSD

    PHP 5.2.4

    Like on most things I go with the solution provided by my framework. Zend Cache offers me some nice frontends and backends, can be directly used on many components of the Zend Framework (Zend_Locale, Zend_Translate, Zend_Db_Table_Abstract, Zend_Paginator and Zend_Feed_Reader) which offers direct support for caches and it is expandable by offering an interface for your own custom frontends or backends.

     

    Opcode/Bytecode Caches

    Opcode-caches are PHP-extensions which save the opcode generated by the compiler, so that for the next call to the file the PHP-Interpreter does not need to compile the PHP-code again into opcodes, it can directly be executed. When using frameworks or libraries with many files, using an opcode cache can really boost the performance. Most opcode-caches include access to the allocated memory which allows using them for caching data directly in the memory too, which is really fast.

    Cache Description Version Systems and PHP-Versions
    APC Probably the best known opcode-cache for PHP, will be part of the PHP-core in PHP6 3.0.19 (stable) (php 4 + 5)
    3.1.3p1 (beta) (php 5, 5.3)
    PHP 4, 5, 5.3 (beta)
    XCache XCache is developed by the lighttpd-Team 1.3 PHP 4, 5, 5.3
    eAccelerator Based on Turck MMCache 0.9.5.3 PHP 4, 5, 5.3
    Windows Cache Extension for PHP Windows/IIS only opcode-Cache 1.0 Windows + IIS only PHP 5.2 / 5.3 x86 (32bit)
    Zend Optimizer+ Zend's Optimizer included in their products like Zend Platform and Zend Server 3.3 PHP 4, 5

    Not in the List because they are out-dated or don't support PHP 5 or newer: ionCube PHP Accelerator, Turck MMCache

    I use APC as opcode-cache, like XCache it is in the Debian/Ubuntu-repository und easily installable via apt-get:

    apt-get install php-apc
    
    apt-get install php5-xcache
    

    And since APC is Part of PECL it is easily installable via pecl:

    pecl install apc
    

    If you want to know which one is the best (speed/stability): test it yourself!
    For the benchmark-loving people here are some links:

    And some informations and slides on caching

    Did I forget something ? Any misspelling ? Know another opcode-cache which should be mentioned ? Another caching-class ? Another tutorial/snippet for one of the caching-classes ?
    Write a comment!

    ]]>
    Thu, 07 Jan 2010 02:33:01 +0000
    <![CDATA[HTTP-Requests with php - An overview of extensions and classes]]> http://www.robo47.net/blog/191-HTTP-Requests-with-php-An-overview-of-extensions-and-classes http://www.robo47.net/blog/191-HTTP-Requests-with-php-An-overview-of-extensions-and-classes PHP offers a lot of ways for requesting (GET) content via http(s) (and other protocols) and sending POST-Requests, if you need it for accessing an API, downloading files, unittests for a webservice, login-requests or whatever.

    The extensions behind all those variantes are sockets (fsockopen()), streams (stream_socket_client()), curl or the PECL Extension Pecl_Http. Not all are usable on every host because of the php-configuration and the compiled/loaded extensions.

    Here is a list of extensions and php-classes which can help you with those topics.

    PHP Http(s) Request Classes

    Class Backend-Extension Features Version /
    PHP Version /
    license
    Snippets / Tutorials
    PEAR Http_Request streams, sockets Authentication (basic)
    Compression (gzip/deflate)
    Cookies
    Encryption (ssl)
    File Uploads
    Proxies (Authentication)
    1.4.4 (2008-11-17) (superseeded by HTTP_Request2)
    PHP 4.2.0
    BSD License
     
    PEAR Http_Request2 streams, ext/curl Authentication (basic, digest)
    Compression (gzip/deflate)
    Cookies
    Encryption (ssl)
    File Uploads
    Proxies (Authentication)
    SSL Certificates
    0.5.1 (alpha) (2009-11-21)
    PHP 5.1.4
    BSD License
     
    PEAR Net_Curl ext/curl Authentication (basic)
    Encryption (ssl)
    Cookies
    File Uploads
    Proxies (Authentication)
    SSL Certificates
    Curl-Access
    1.2.5 (2008-05-03) (superseeded by HTTP_Request2)
    PHP 4.2.0
    New BSD License
     
    PECL Pecl_Http ext/Pecl_Http
    (needs ext/curl)
    Authentication (basic, digest, GSS-Negotiate, NTLM)
    Compression (gzip/deflate)
    Cookies
    Encryption (ssl)
    Filesize and Speed-limits (min/max) for Requests
    File Uploads
    Multiple synchron Requests (HttpRequestPool)
    Proxies (Authentication)
    SSL Certificates
    1.6.6 (2009-12-10)
    PHP 4.3
    New BSD License
     
    Zend_Http_Client streams, ext/curl Authentication (basic)
    Encryption (ssl)
    Cookies
    Curl-Access
    File Uploads
    Proxies (Authentication)
    Extendable with additional Adapters
    1.9.6 (2009-11-23)
    PHP 5.2.4
    New BSD License
    Snippet for File-Upload with Zend_Http_Client
    Wordpress WP_Http / WP_Http_Curl sockets, ext/curl Encryption (ssl)
    Compression (gzip/deflate)
    Cookies
    Proxies (Authentication)
    2.8.6 (2009-11-12)
    PHP 4.3
    GPL v2
     
    Curl ext/curl Authentication (basic, digest, GSS-Negotiate, NTLM)
    Big File Downloads
    Cookies
    Encryption (ssl)
    File Uploads
    FTP(S) Upload/List
    Many other protocols (HTTP, HTTPS, LDAP, LDAPS, FTP, FTPS, TFTP, SCP, SFTP)
    Multiple synchron requests (curl_multi)
    Proxies (Authentication)
    SSL Certificates
    libcurl-version: 7.19.7 (2009-11-04)
    PHP 4 (some Options need 5.0, 5.1 or 5.2.10)
    extension: PHP-License
    libcurl: MIT/X
    Snippet for File-Upload with curl
    Snoopy sockets, curl-binary Authentication (basic)
    Cookies
    Encryption (ssl)
    File Uploads
    Proxies (Authentication)

    1.2.4 (2008-10-22)
    PHP 4
    LGPL

    Snippet for File-Upload with Snoopy
    CakePHP HttpSocket sockets Encryption (ssl)
    Cookies
    1.2.5 (2009-09-08)
    PHP 4.3.2
    MIT License
     

    Some people will notice that not all Classes which use Curl are capable of all Features curl offers, that's because I only mentioned features the classes/functions offer itself (even when using only sockets) and in some classes you can't even access the curl-resource, WP_Http_Curl for example creates the resource insides as a local variable which you can't manipulate, so you can't use setopt to set all the Options possibly needed by curl, Snoopy doesn't use ext/curl, it uses the curl-binary if available for some things. I added the feature "Curl-Access" if the classes allow to inject the curl-instance or allows to directly manipulate the curl-instance-options, which doesn't mean you can use every feature of curl with this class, but at least can change some more settings and parameters.

    Some of the curl-extensions abilitys (protocols like scp, sftp) are only available if the newest version of libcurl is used and if libcurl was compiled with the right flags and the right libs where intalled (like libssh2 for scp-support) and controlling some Features depends on the php-version. For more information on curl look in the php-manual.


    I used Snoopy for a long long time, it is simple and worked for most requests, since I began using the Zend Framework I switched to Zend_Http_Client, only on rare occasion if I need special features like the multiple synchron requests or downloading files bigger than memory_limit, i use a simple curl/curl_multi wrapper.

    Did I forget something ? Any misspelling ? Do you know another Class for HTTP-Requests ? Or some Tutorials / Snippets for one of the listed ?
    Write a comment !

    ]]>
    Thu, 07 Jan 2010 01:54:30 +0000
    <![CDATA[Ctrl + Alt + Backspace for restarting x-server on debian squeeze working again]]> http://www.robo47.net/blog/190-Ctrl-Alt-Backspace-for-restarting-x-server-on-debian-squeeze-working-again http://www.robo47.net/blog/190-Ctrl-Alt-Backspace-for-restarting-x-server-on-debian-squeeze-working-again Noticed it some time ago that like in ubuntu (since 9.04), debian doesn't restart the x-server if you hit ctrl + alt + backspace anymore.

    It was deactivated in the X-Server some time ago because people thought it was pushed to often accidently, especially by former windows users. I think I never pushed it accidently, but well, as long as it is easy reactivateable it is no problem.
     

    On ubuntu this could be fixed by installing dontzap via apt-get/apitude or synaptic and running "dontzap --disable". I only tried that since I remembered it, but it didn't work in debian because the package didn't exist. I didn't look into it anymore, but today I accidently found an option while klicking through the keyboard-settings which reactivates it under debian squeeze:

    Preferences -> System -> Keyboard -> Layouts (Tab) -> "Layout Options" (Button bottom right) -> "Key sequence to kill the X Server" -> "Control + Alt + Backspace".

    ]]>
    Thu, 07 Jan 2010 00:50:18 +0000
    <![CDATA[Installing PHC 0.3.2 and ThinkPad Fan Control (tpfand) 0.9.5 on Debian Squeeze for my intel-based R52 Thinkpad]]> http://www.robo47.net/blog/189-Installing-PHC-0.3.2-and-ThinkPad-Fan-Control-tpfand-0.9.5-on-Debian-Squeeze-for-my-intel-based-R52-Thinkpad http://www.robo47.net/blog/189-Installing-PHC-0.3.2-and-ThinkPad-Fan-Control-tpfand-0.9.5-on-Debian-Squeeze-for-my-intel-based-R52-Thinkpad Since I switched from Ubuntu 9.04 to Debian 6.0 Squeeze (testing) a week ago I missed the phc-module for undervolting my intel cpu and tpfand to control the fan-speed based on temperatures of sensors on my thinkpad (R52 - 1846-64G)

    On Ubuntu 9.04 I used a ppa-repository from launchpad like it was described here.

    Since there was no repository for Debian Squeeze I just build the module with the help of phcs build-script (should support kernel 2.6.27, 2.6.28, 2.6.29, 2.6.30, 2.6.31 and 2.6.32, my squeeze runs 2.6.30-2-686)

    Don't forget, changing cpu's core-voltage or changing the fans speed and temperature-control will void your hardware's waranty and can destroy your hardware, so don't do it if you don't know what you are doing!

    Download phc 0.3.2-9 for intel from the phc forum

    Extract it, open a shell, switch user to root (su / sudo) change to the directory and run:

    make prepare
    make
    make install
    

    if everything went well, unload the old acpi-cpufreq and load the new phc-intel:

    modprobe -r acpi_cpufreq
    modprobe phc-intel
    

    The changes to /etc/modprobe.d/ are automatically in phc-intel.conf.

    If you don't have a config yet and want a gui you can try PHCTool 0.5.2-2, which allows setting values and reading them too (needs kernel module msr to be loaded)

    PHCTool 5.2-2

    My current config (.phcstore.phc) for my pentium 4 mobile 1.73 ghz is:

    R=VT
    0: V=23 18 8 1 T=0
    

    But it is important to test what your cpu is capable of running stable at.


    For tpfand i used the tpfand - 0.95-ubuntu1 deb for ubuntu jaunty and the tpfand-admin 0.95 from the tpfand-page which can be build easily (ubuntu-debs of the admin didn't work because of the missing dependency python-gnome2-desktop >= 2.22):
    But package python-gnome2-desktop-dev needs to be installed on debian
    Download tpfan-admin.0.95.tar.gz
    Extract it, open a shell, switch user to root (su / sudo) change to the directory and run:

    make install
    

    then you can start it with

    tpfan-admin
    

    Since my config is still on the backup-drive i created one fast setting fan off below 50°C to 15 % if any sensor is above 50°C and wents back to hardware-control if hotter than 60°C:

    enabled = True
    override_profile = True
    
    0. Sensor 0 = 0:0 50:2 60:255
    1. Sensor 1 = 0:0 50:2 60:255
    2. Sensor 2 = 0:0 50:2 60:255
    3. Sensor 3 = 0:0 50:2 60:255
    4. Sensor 4 = 0:0 50:2 60:255
    5. Sensor 5 = 0:255
    6. Sensor 6 = 0:0 50:2 60:255
    7. Sensor 7 = 0:255
    8. Sensor 8 = 0:0 50:2 60:255
    9. Sensor 9 = 0:0 50:2 60:255
    10. Sensor 10 = 0:0 50:2 60:255
    11. Sensor 11 = 0:255
    12. Sensor 12 = 0:255
    13. Sensor 13 = 0:255
    14. Sensor 14 = 0:255
    15. Sensor 15 = 0:255
    
    hysteresis = 2
    interval_speed = 2
    interval_duration = 500.000000
    interval_delay = 5000.000000

    My thinkpad is now running 10+ degrees cooler, fan has less to work and the battery lasts about an hour longer again. Now I am happy again :)

    ]]>
    Mon, 28 Dec 2009 12:59:21 +0000
    <![CDATA[Zend Framework Survey 2009 and Roadmap for Zend Framework 2.0 - Win an Ipod]]> http://www.robo47.net/blog/188-Zend-Framework-Survey-2009-and-Roadmap-for-Zend-Framework-2.0-Win-an-Ipod http://www.robo47.net/blog/188-Zend-Framework-Survey-2009-and-Roadmap-for-Zend-Framework-2.0-Win-an-Ipod Ralf Eggert announced it already 2 days ago and now it appeared at Zend's Devzone: Matthew Weier O'Phinney calls for people in contributing to the Zend Framework Survey 2009.

    You can even win an Ipod if you leave your email! :)

    ]]>
    Wed, 16 Dec 2009 23:58:26 +0000
    <![CDATA[Free PHP-Posters - SektionsEins PHP Security Poster and Mayflowers Zend Framework Poster]]> http://www.robo47.net/blog/187-Free-PHP-Posters-SektionsEins-PHP-Security-Poster-and-Mayflowers-Zend-Framework-Poster http://www.robo47.net/blog/187-Free-PHP-Posters-SektionsEins-PHP-Security-Poster-and-Mayflowers-Zend-Framework-Poster Propably I am really a bit late, but today I found my Zend Framework-Poster I though I never received, somebody had put the envelope into the wrong shelf and this week I got my SektionEins PHP security poster.
    SektionsEins (Stefan Esser) currently offers a free PHP security-poster you can order on the SektionEins-homepage and Mayflower offers a Zend Framework Poster.

    More information about the posters you find on the suspect.org-blog by Stefan Esser and on the thinkphp blog. So if you interested and are willing to give away your name and adress, order them.
    They are both available in english and german.

    I have to say thanks to SektionEins and Mayflower for those 2 great Posters, my only problem currently is how to get them both on my walls, I would have prefered to put them both on the ceiling above my desk, but since we have a wooden-ceiling with lots of crossbeams, that is impossible, I think I will have to rearrange some stuff in my room to place them somewhere I can read both without turning around or standing up.

    ]]>
    Sat, 12 Dec 2009 11:50:22 +0000
    <![CDATA[Netbeans 6.8 got released]]> http://www.robo47.net/blog/185-Netbeans-6.8-got-released http://www.robo47.net/blog/185-Netbeans-6.8-got-released Netbeans 6.8 got released today, from the PHP-Perspective Netbeans 6.8 now Supports PHP 5.3 and has Integration for the Symfony Framework, Code-Completion seems to be a lot faster and more.

    Complete list for PHP-Part

    Complete List for Java and other languages: New and Noteworthy Netbeans 6.8

    There too exists a Netbeans Promotion Video on the Sun Channel Homepage and a Symfony Support Screencast on the Netbeans PHP Blog.

     

    ]]>
    Thu, 10 Dec 2009 19:23:38 +0000
    <![CDATA[Doctrine 1.2.1 is out]]> http://www.robo47.net/blog/184-Doctrine-1.2.1-is-out http://www.robo47.net/blog/184-Doctrine-1.2.1-is-out Doctrine 1.2.1 was released some hours ago, it contains some bugfixes:

    • [r6834] Fixing issue with relationship ordering
    • [r6835] Fixes issue with oracle adapter statement using wrong constant
    • [r6836] Fixing issue with sfYaml autoload not returning true
    • [r6839] Fixes issue with array cache driver and deleting
    • [r6840] Fixed thrown Exceptions to be package-level
    • [r6842] Fixes issue with $length in migrations addColumn
    • [r6859] Fixed misplaced param when parsing join condition
    • [r6883] Added empty init() method implementation to avoid method does not exist error
    • [r6889] Fixing issue with nested set createRoot() method and string root column
    • [r6893] Adding _cleanup() call to start of migrations diff to make sure directory is clean

    Full Changelog: Change Log Doctrine 1.2.1
    Download: Download Doctrine 1.2.1.

    ]]>
    Tue, 08 Dec 2009 03:24:20 +0000
    <![CDATA[My default settings for Netbeans with PHP]]> http://www.robo47.net/blog/183-My-default-settings-for-Netbeans-with-PHP http://www.robo47.net/blog/183-My-default-settings-for-Netbeans-with-PHP Some default settings I use in Netbeans:

    Hiding and Showing specific Files

    Because I want to see my .htaccess-Files in the Project-View, dont want to see the .git-directory but want to see .gitignore for example I change the Patter used to hide files in the Projects-View.

    Tools -> Options -> Miscellaneous -> Files -> Files Ignored by the IDE -> Ignored Files Pattern

     ^(CVS|SCCS|vssver.?\.scc|#.*#|%.*%|_svn)$|~$

    Twilight Theme (Dark Theme) for Netbeans

    Since I prefer dark themes for my OS and my tools, I am using the Twilight-Theme for Netbeans.
    Only the default-font I change back to Monospaced.

    Tools -> Options -> Import -> Netbeans_Twilight_Theme.zip
    Restart Netbeans and go to

    Tools -> Options -> Fonts & Colors and Choose it from the Profile-Selection.

    Follow currently edited File

    Netbeans supports following the currently in the editor opened file in the File-Tree of the project:

    View -> Synchronize Editor With Views

    Shortcut for "Remove Trailing Spaces"

    Setting a short-cut for "Remove Trailing Spaces"(In most cases i forget to use it, but my ANT-Job does it later for me) to Shift + Ctrl + R in Tools -> Options -> Keymap

    Activating additional PHP-Hints

    Activating some Hints for PHP like "Variable might have not been initialized" or "Variable does not seem to be used in its scope":
    Tools -> Options -> Editor -> Hints
    Check or Uncheck the wanted Hints.

    When working with big projects or while running something else in background I often turn off those Hints because my notebook is old and slow (P4 Mobile 1.73 Ghz, 2 GB RAM, 120GB Drive @ 5400 RPM running Ubuntu 9.04) and it can be a pain to work with it all features turned on.

    CodeSniffer Plugin

    Downloading and Installing CodeSniffer Plugin

    Tools -> Plugins -> Downloaded

    ]]>
    Tue, 01 Dec 2009 04:14:02 +0000
    <![CDATA[Problems with dark themes for you operating system]]> http://www.robo47.net/blog/182-Problems-with-dark-themes-for-you-operating-system http://www.robo47.net/blog/182-Problems-with-dark-themes-for-you-operating-system When choosing a dark theme for your operating system, the biggest problem often is to find one which plays nice with all programs and tools and the OS itself. Big problem are often, that Tools use black text on black or dark background which is in most cases unreadable and forces you to select texts to read them (if possible and marked text has a better contrast). Another problem you have to face is that the text-color and background-color of select elements match, which again results in unreadable text. Marked text can get unreadable too if the marked color matches the color of marked text ...
    Something really annoying when using dark themes is that some tools don't use all colors from the theme, for example force white backgrounds for text-based stuff, but that's something you can't really change when it is hardcoded in Programs or you have to look if they offer extra options for their color.

    One thing you will propably start to notice when browsing the web, that browsers like Firefox use the the systems background-as default-css-background-color for webpages and form-elements instead of white and that means some webpages (which forget to set a background-color for html/body) will have the background-color of your theme instead of white. That can look really strange and sometimes really sucks on Forms if you can't even read what you have written.

    On Linux/Gnome I used the Clearlooks Darklime Theme for a long time, it propably is more a semi-dark-theme than a dark theme, but it is nice and didn't have any glitches that disturbed me. I already used it for GTK-Applications (like Pidgin) under Windows XP more than a Year ago and i was happy with it. Currently I am using the Murrina LimeLight Theme, which is darker but also features green.

    Looking for some other dark Themes for Gnome ? Try 20 beautiful dark themes for Gnome and Ubuntu on Cats who Code

    ]]>
    Tue, 01 Dec 2009 03:29:46 +0000
    <![CDATA[Netbeans - Features for Netbeans 6.9]]> http://www.robo47.net/blog/181-Netbeans-Features-for-Netbeans-6.9 http://www.robo47.net/blog/181-Netbeans-Features-for-Netbeans-6.9 The Netbeans PHP Team is currently looking for new features for the next Release of Netbeans

    Looking at the posts Planning Features for Netbeans next, Planning Features for Netbeans next ... Continuation I and Planning Features for Netbeans next ... Continuation II on the Netbeans PHP-Team-Blog shows some interesting ideas like integration of optimizers for css/js (minifying/compression). There are already over 60 comments to the 3 threads.

    I would be really happy if there would an Integration for the Zend Framework like it exists for Symfony already, the issue for Zend Framework-integration currently has more than 100 votes.

    Other features I am waiting for which are not really PHP-features are the integration of my prefered VCS GIT and a better integration for removing trailing spaces. Like I have written in a Comment to Remove Trailing Spaces I am currently using an ANT-Task which uses the unix-command sed to remove all trailing spaces from my php-Files, but that's only a workaround, no solution.

    So if you have any other good ideas from which the PHP-Developers unsing Netbeans would profit -> tell them!

    ]]>
    Tue, 01 Dec 2009 01:55:49 +0000
    <![CDATA[Doctrine 1.2 stable released]]> http://www.robo47.net/blog/180-Doctrine-1.2-stable-released http://www.robo47.net/blog/180-Doctrine-1.2-stable-released Doctrine 1.2 stable was released yesterday.

    It is a LTS-Release (Long Time Support-Release) which will be supported for 18 month until 06/01/2011.

    An overview of the new features can be found here.

    Grab your copy at the download page of Doctrine ORM.

    ]]>
    Tue, 01 Dec 2009 01:26:09 +0000
    <![CDATA[PHP Advent Calendar 2009]]> http://www.robo47.net/blog/179-PHP-Advent-Calendar-2009 http://www.robo47.net/blog/179-PHP-Advent-Calendar-2009 Started by Chris Shiflett and Sean Coates the php-advent calendar offers an articles (not only directly php-related stuff) for each day.

    Last year's (2008) articles were contributed by Chris Cornutt, Matthew Weier O’Phinney, Travis Swicegood, Lorna Mitchell, Cal Evans, Eli White, Keith Casey, Helgi Þormar Þorbjörnsson, Ivo Jansch, Paul Jones, Ed Finkler, Maggie Nelson, Marco Tabini, Ligaya Turmelle, Andrei Zmievski, Scott MacVicar, Elizabeth Smith, Ben Ramsey, Luke Welling, Paul Reinheimer, Derick Rethans, Nate Abele and Terry Chay.

    Let's be prepared for the the 2009 calendar and see what it brings and who will contribute this year.

    You can subscribe to the Feed or you can follow the phpadvent-twitter-account.

    And if you have some time, read the articles from 2008 or 2007.

    ]]>
    Sun, 29 Nov 2009 15:27:25 +0000
    <![CDATA[Articles, Snippets and Libraries for using Doctrine with the Zend Framework]]> http://www.robo47.net/blog/178-Articles-Snippets-and-Libraries-for-using-Doctrine-with-the-Zend-Framework http://www.robo47.net/blog/178-Articles-Snippets-and-Libraries-for-using-Doctrine-with-the-Zend-Framework Since Doctrine will be integrated into the Zend Framework and Doctrine 2.0 in Zend Framework 2.0 (Both will need PHP 5.3), I thought it would be nice to have have some informations about how you can already use Doctrine in your Zend Framework Applications, what code-snippets exist out there and what you don't need to reinvent.
    There is already a lot of Articles about how to use Doctrine as an ORM System in Zend Framework-Applications, some snippets, even a podcast, a libary and the Doctrine Manual for informations about the usage of Doctrine itself.

    Some of the blog-articles overlap in their content because they talk about the same basic things like auto-loading or resources for bootstrapping with Zend_Application, but since there always different ways for achieving a goal and not everybody prefers the same I thought listing different approaches won't harm anybody.

    Blog-Articles

    Podcasts

    Plugins

    Snippets / Code / Classes

    Libraries

    Manuals

     

    Know any other resources for using Doctrine in the Zend Framework ? Write a comment!

    ]]>
    Sat, 28 Nov 2009 21:42:29 +0000
    <![CDATA[Technische Artikel in Zukunft in Englisch]]> http://www.robo47.net/blog/177-Technische-Artikel-in-Zukunft-in-Englisch http://www.robo47.net/blog/177-Technische-Artikel-in-Zukunft-in-Englisch Nach einer längeren Überlegung bin ich zu dem Schluss gekommen, dass es mehr Sinn macht meine technischen Artikel, Code-Schnipsel und sonstigen Inhalte die sich um Dinge wie Webprogrammierung, Apache, Linux, PHP, Doctrine, Zend Framework oder ähnliches handeln in Englisch zu verfassen.

    Gründe gibt es da viele, zuerst mal gab es schon ein paar Anfragen/Wünsche ob ich Artikel nicht auch in Englisch veröffentlichen könnte, weil die Leser kein Deutsch konnten, ausserdem ergab eine Auswertung der Referrer meiner Seite, dass einige Artikel mehr Aufrufe über Google Translate oder auch ausländische Suchmaschinen haben als normale Aufrufe (auch der ein oder andere Link geht direkt auf Google Translate anstatt auf den Artikel). Das alles hat mir zu denken gegeben und ich bin zu dem Ergebnis gekommen dass ich dadurch auch die Anzahl meiner potentiellen Leser durchaus vergrößern kann, ausserdem ist es für mich gleichzeitig eine gute Übung etwas mehr in Englisch zu schreiben, nachdem schon die meisten Bücher im technischen und teilweise auch privat Bereich die ich lese Englisch sind, tut mir ein wenig mehr Englisch schreiben sicher auch gut.

    Der Aufwand alle Artikel und Einträge in Deutsch und Englisch zu schreiben ist mir definitiv zu groß, gerade bei Updates von Artikeln würde ich da sicher irgendwann die Lust verlieren, als Fallback bau ich vielleicht Links auf Google Translate ein oder schau mal was es da noch so an Alternativen gibt.

    Vielleicht werde ich bei Zeit und Lust auch den ein oder anderen alten Artikel überarbeiten und zusätzlich in Englisch anbieten, mal schauen.

    Korrekturen betreffs Rechtschreibung und Grammatik sind dann natürlich auch immer gerne gesehen, man will ja auch was lernen.

    ]]>
    Sat, 28 Nov 2009 21:00:03 +0000
    <![CDATA[Adapter für Doctrine_Cache zu Zend_Cache]]> http://www.robo47.net/codeschnipsel/32-Adapter-fuer-Doctrine_Cache-zu-Zend_Cache http://www.robo47.net/codeschnipsel/32-Adapter-fuer-Doctrine_Cache-zu-Zend_Cache Dieser Adapter erlaubt es eine schon vorhandene Instanz eines Zend_Cache_Core-Objects als Cache für Doctrine und dessen Query und Result-Cache zu nutzen. So ist es mit Doctrine möglich auch Backends zu nutzen die Doctrine nicht bietet (ZendCache, ZendServer oder den TwoLevel-Cache mit verschiedenen Adaptern) und man spart sie die Resourcen für eine Extra-Instanz von Doctrine_Cache.

    Ausserdem ermöglicht der Adapter ein automatisches Prefixing aller Einträge um ein einfaches löschen aller mit Doctrine assoziierten Cache-Einträge auch ohne Tags zu ermöglichen.



    <?php
    
    /**
     * An Adapter for using a Zend_Cache_Core-Instance as Query or Result-Cache
     * for Doctrine
     *
     * Offers an additional Prefix for its entries for usage within prefix-based
     * Cache-Structure (for example when using one Zend_Cache_Core for a complete
     * system)
     *
     * @uses       Doctrine_Cache_Interface
     * @author     Benjamin Steininger
     * @license    New BSD License
     * @category   Robo47
     * @package    Robo47_Cache
     * @todo       Add support for Tags to automatically tag all Entry made with a
     *             set of Tags provided by the constructor
     */
    class Robo47_Cache_DoctrineAdapter implements Doctrine_Cache_Interface
    {
        /**
         * @var Zend_Cache_Core
         */
        protected $_cache = null;
    
        /**
         * @param string
         */
        protected $_prefix = '';
    
        /**
         * @param Zend_Cache_Core $cache
         * @param string $prefix
         */
        public function __construct(Zend_Cache_Core $cache, $prefix = '')
        {
            $this->_cache = $cache;
            $this->_prefix = $prefix;
        }
    
        /**
         * Test if a cache is available for the given id and (if yes) return it
         * (false else)
         *
         * Note : return value is always "string" (unserialization is done by the
         * core not by the backend)
         *
         * @param string    $id cache id
         * @param boolean   $testCacheValidity  if set to false, the cache
         *                                      validity won't be tested
         * @return string  cached datas (or false)
         */
        public function fetch($id, $testCacheValidity = true)
        {
            $id = $this->_prefix . $id;
            return $this->_cache->load($id, $testCacheValidity);
        }
    
        /**
         * Test if a cache is available or not (for the given id)
         *
         * @param string  $id cache id
         * @return mixed  false (a cache is not available) or "last modified"
         *                timestamp (int) of the available cache record
         */
        public function contains($id)
        {
            $id = $this->_prefix . $id;
            return $this->_cache->test($id);
        }
    
        /**
         * Save some string datas into a cache record
         *
         * Note : $data is always saved as a string
         *
         * @param string $id           cache id
         * @param string $data         data to cache
         * @param int    $lifeTime     if != false, set a specific
         *                             lifetime for this cache record
         *                             (null => infinite lifeTime)
         * @return boolean true if no problem
         */
        public function save($id, $data, $lifeTime = false)
        {
            $id = $this->_prefix . $id;
            return $this->_cache->save($data, $id, array(), $lifeTime);
        }
    
        /**
         * Remove a cache record
         *
         * @param string $id cache id
         * @return boolean true if no problem
         */
        public function delete($id)
        {
            $id = $this->_prefix . $id;
            return $this->_cache->remove($id);
        }
    }
    ]]>
    Sat, 28 Nov 2009 17:08:47 +0000
    <![CDATA[Zend Framework - HTMLPurifier Autoloader]]> http://www.robo47.net/codeschnipsel/31-Zend-Framework-HTMLPurifier-Autoloader http://www.robo47.net/codeschnipsel/31-Zend-Framework-HTMLPurifier-Autoloader HTMLPurifier hat ein etwas komplexeres Autoloading-Verhalten wie das Zend Framework, dabei werden z.b. für Sprachen andere Pfadstrukturen verwendet wie bei normalen Klassen. Da ich HTMLPurifier für das Filtern der Kommentare nutze hab ich auch hierfür einen Loader gebraucht der im Aufbau genauso funktioniert wie der für Komponenten des ezComponents-Framework, der Autoloader von HTMLPurifier wird einmalig händisch includiert und dann wird diesem der Klassennamen immer weitergegeben um die Klasse zu laden.

    Einfach in einer Init-Methode des Bootstraps mit folgendem Code den Loader einbinden:

    $autoLoader = Zend_Loader_Autoloader::getInstance();
    $autoLoader->pushAutoloader(new App_Loader_Autoloader_HTMLPurifier(), 'htmlpurifier');
    

    Dann werden alle Klassen die mit HTMLPurifier beginnen automatisch vom Autoloader der HTMLPurifier-Bibliothek geladen.



    <?php
    
    /**
     * Autoloader for HTMLPurifier
     *
     * @author Benjamin Steininger <robo47@robo47.net>
     */
    class App_Loader_Autoloader_HTMLPurifier implements Zend_Loader_Autoloader_Interface
    {
        /**
         * If the needed class/file is already loaded
         *
         * @var bool
         */
        private $_loaded = false;
    
        /**
         * Autoload-Method
         *
         * @param string $class name of the class
         */
        public function autoload($class)
        {
            if(!$this->_loaded) {
                require_once 'HTMLPurifier/Bootstrap.php';
                $this->_loaded = true;
            }
            HTMLPurifier_Bootstrap::autoload($class);
        }
    }
    ]]>
    Wed, 18 Nov 2009 22:32:00 +0000
    <![CDATA[Zend Framework - ezComponents Autoloader]]> http://www.robo47.net/codeschnipsel/30-Zend-Framework-ezComponents-Autoloader http://www.robo47.net/codeschnipsel/30-Zend-Framework-ezComponents-Autoloader Da das Autoloading von Komponenten des ezComponents-Frameworks etwas anders abläuft als bei Komponenten des Zend Framework (Klassenname lässt sich in Pfad + Dateiname umwandeln) und ich innerhalb dieses Blogs auch ezcGraph einsetze, hab ich mir einen passenden Autoloader für ezComponents-Komponenten innerhalb des Zend Frameworks gebastelt der das Laden der Komponenten an den Autoloader des ezComponents-Frameworks (ezcBase::autoload()) weitergibt und vorher die benötigte Klasse selbst lädt.

     

    Einfach in einer Init-Methode des Bootstraps mit folgendem Code den Loader einbinden:

    $autoLoader = Zend_Loader_Autoloader::getInstance();
    $autoLoader->pushAutoloader(new App_Loader_Autoloader_Ezc(), 'ezc');
    

    Dann werden alle Klassen die mit ezc beginnen automatisch vom Autoloader des ezComponents-Frameworks geladen.



    <?php
    
    /**
     * Autoloader for ezComponents
     *
     * @author Benjamin Steininger <robo47@robo47.net>
     */
    class App_Loader_Autoloader_Ezc implements Zend_Loader_Autoloader_Interface
    {
        /**
         * If the needed class/file is already loaded
         *
         * @var bool
         */
        private $_loaded = false;
    
        /**
         * Autoload-Method
         *
         * @param string $class name of the class
         */
        public function autoload($class)
        {
            if(!$this->_loaded) {
                require_once 'ezc/Base/src/base.php';
                $this->_loaded = true;
            }
            ezcBase::autoload($class);
        }
    }
    ]]>
    Wed, 18 Nov 2009 22:31:38 +0000
    <![CDATA[Roadmap für Zend Framework 2.0 und Integration von Doctrine]]> http://www.robo47.net/blog/176-Roadmap-fuer-Zend-Framework-2.0-und-Integration-von-Doctrine http://www.robo47.net/blog/176-Roadmap-fuer-Zend-Framework-2.0-und-Integration-von-Doctrine Matthew Weier O’Phinney hat gestern die Roadmap für das Zend Framework 2.0 im Community-Wiki des Zend Frameworks vorgestellt und in den letzten 2 Wochen hat sich dann auch ergeben, dass Doctrine wohl etwas mehr Unterstützung im Zend Framework bekommen wird.

    Mit dem Zend Framework 2.0 will man die neue Features die PHP 5.3.x bietet nutzen, dazu gehören unter Anderem Namespaces, Late Static Binding (LSB), Closures, goto (ich hoffe ja die haben einen Schutz vor Raptoren:  XKCD: Goto). 

    Release-Termine oder ähnliches gibt es natürlich noch nicht, ZF 1.0 wird wohl auch noch das ein oder andere Major und Minor Release vorher erfahren denke ich.

    Worauf ich mich im 1.0er Zweig jedoch am meisten freut ist die stärkere Integration von Doctrine. Benjamin Eberlei (Sein Blog: http://www.whitewashing.de/) der sich eigentlich um die neue Entwicklung von Zend_Entity kümmern sollte, hat beschlossen statt dessen seine Zeit in die Integration von Doctrine zu stecken, damit wird es wohl in ZF 1.10 oder ZF 1.11 neben Bootstrap-Resourcen auch Zend_Tool-Integration für die Doctrine-CLI, Adapter für Zend_Log_Writer, Zend_Auth_Adapter, Zend_Session_SaveHandler und Zend_Paginator_Adapter geben.

    Ausserdem gibt es im Wiki eine Seite wo aktuell alles zur Integration von Doctrine diskutiert wird: Doctrine Integration TODO.

    ]]>
    Thu, 12 Nov 2009 13:34:59 +0000
    <![CDATA[Updates und Umstellung der RSS-Feeds]]> http://www.robo47.net/blog/175-Updates-und-Umstellung-der-RSS-Feeds http://www.robo47.net/blog/175-Updates-und-Umstellung-der-RSS-Feeds So, mal wieder ein paar Neuerungen ins Live-System übertragen:

    Sowohl das Zend Framework (1.9.5), als auch Doctrine (1.2.0ALPHA3) und ezComponents (2009.1.2) wurden auf die aktuellsten Versionen geupdatet. Im Backend sowie Frontend kommt jetzt der CKEditor als WYSIWYG-Editor zum Einsatz, im Frontend bei den Kommentaren und im Backend bei allen HTML-Inhalten. Neben dem schon länger vorhandenen Tweet This-Button gibt es jetzt auch einen Dent-This Button für Identi.ca User (An einer Integration für Pingbacks z.b. von Identi.ca arbeite ich noch). Ausserdem werden meine Eigenen Kommentare jetzt etwas hervorgehoben.

    Die größte Änderung gab es bei den Feeds. Hier habe ich jetzt Blogeinträge, Texte, Codeschnipsel und Tools in einen Feed gemergt.

    Die aktuellen Urls zum normalen Feed und zum Kommentar-Feed sind:
    http://www.robo47.net/feed/content.rss

    http://www.robo47.net/feed/comments.rss

    Desweiteren bietet auch jeder Text, Blogeintrag, Codeschnipsel und jedes Tool jeweils einen eigenen Kommentar-Feed, falls man nur an Kommentaren zu einem einzelnen Eintrag interessiert ist.

    Die alten Feed-Urls werden natürlich noch eine Zeit lang normal laufen und dann erstmal via 301er Header auf die neuen Urls umgeleitet, wer also einen Reader hat der keinen 301er Header unterstützt sollte am besten gleich die Url updaten. Irgendwann werde ich dort dann nur noch einen Fehlermeldungsbeitrag ausgeben um auch die letzten übriggebliebenen auf die neuen Feeds aufmerksam zu machen bevor ich sie dann abschalte.
    Wer übrigends noch via Feedburner meinen Feed abonniert, sollte auch auf die neuen Urls umstellen, da vorhabe Feedburner auch irgendwann abschalten.

    Die J/K-Navigation auf Basis von JQuery die ich testweise eingebaut hatte hab ich wieder rausgenommen nachdem mich jemand darauf hingewiesen hatte, dass man damit kein j und kein k mehr in den Text-Feldern tippen konnte, da werde ich auch mal schauen inwieweit ich das was gebastelt bekomme das den Focus noch überprüft.

    Aus technischer Sicht steht dann wohl als nächstes mal die Unterstützung für Pingbacks auf dem Plan (Hoffentlich tut sich sich da bald was bei Zend_Service_Linkback) und die seit ZF 1.8 nicht mehr lauffähigen Unittests (sowie neue schreiben für alles was seitdem dazugekommen ist) will ich auch mal wieder reaktivieren und zum Teil neu schreiben. Allgemein steht einiges an Refactoring hinter den Kulissen an wozu ich laufende Unittests begrüßen würde. Und wenn das abgeschlossen ist werde ich wohl mal etwas intensiver in Richtung Nutzung von JQuery und/oder Dojo bei den ganzen Tools gehen. Und nebenbei muss dann irgendwoher noch ein neues Design kommen :) Der angepeilter Termin liegt irgendwo zwischen 2020 und 2030 :)

    ]]>
    Wed, 28 Oct 2009 01:35:31 +0000
    <![CDATA[Apache Access Log Formate - common, combined, combinedio, forensic - Vorteile und Tools zur Auswertung]]> http://www.robo47.net/text/41-Apache-Access-Log-Formate-common-combined-combinedio-forensic-Vorteile-und-Tools-zur-Auswertung http://www.robo47.net/text/41-Apache-Access-Log-Formate-common-combined-combinedio-forensic-Vorteile-und-Tools-zur-Auswertung Apache bietet standardmäßig 2 verschiedene Formate für die Zugriffs-Log-Dateien an, das ist common und combined. In Kombination mit mod_logio (Üblicherweise mitkompiliert) kommt noch combinedio hinzu, mit mod_log_forensic steht auch das forensic-Format zur Verfügung und über die Direktive LogFormat ist es auch möglich seine eigenen Formate so zusammenstellen wie man braucht.

    Der Unterschied zwischen den Formaten besteht in den Informationen die geloggt werden.

    Das common-Format

    Common bietet insgesamt am wenigsten Informationen an:

    • IP
    • Benutzer
    • Datum
    • Request
    • Statuscode
    • Größe

    Die IP sollte klar sein, Benutzer wird im Falle einer Authentifizierung durch eines der Authentifikations-Module (mod_authn_alias, mod_authn_anon, mod_authn_dbd, mod_authn_default, mod_authn_file oder mod_authnz_ldap) des Apachen mit dem Benuternamen gefüllt, das Datum sollte auch klar sein. Der Request enthält Informationen über die Art des Requests (HEAD, GET, POST, PUT) den Pfad + Dokument + Parameter [GET] an den sich der Request gerichtet hat und ob es sich um HTTP 1.0 oder HTTP 1.1 handelt. Mit Größe ist die Anzahl an Bytes die das übertragende Dokument hat gemeint (ohne Header).

    Das combined-Format

    Combined baut auf Common auf und besitzt 2 zusätzlich Felder:

    • Referrer
    • UserAgent

    Referrer wird dabei mit dem Wert des Referer-Headers gefüllt wenn der Client diesen mitsendet und UserAgent ist die Signatur des Browsers des Clients falls dieser den Header mitsendet.

    Das combinedio-Format von mod_logio

    Combinedio baut wie der Name schon erahnen lässt wiederrum auf Combined auf und erweitert dieses um 2 weitere Felder:

    • Input
    • Output

    Input ist dabei die Anzahl an Bytes die bei der Anfrage des Clients an den Server über die Leitung gegangen sind, Output ist die Anzahl der Bytes der kompletten Antwort vom Server.

    Im Gegensatz zur reinen Größe des Dokuments das auch in Common und Combined vorkommt, kommen hier auch die Header die der Client bei der Anfrage und der Server bei seiner Antwort mitsendet zum tragen, ausserdem erscheint hier auch der entstandene Traffic der durch Dateien die z.b. via Dateiupload hochgeladen wurden oder auch Dateien die via PUT (z.b. mod_svn) hochgeladen wurden.

    Das forensic-Format von mod_log_forensic

    Das Forensic-Format loggt zwar nicht wie combinedio die Größe der Header bei Request und Response, aber dafür alle Header und deren Werte. Jedem Request wird eine eindeutige ID zugewisen die im Log erscheint, ausserdem wird bei jedem Request doppelt geloggt einmal sobald der Request ankommt, also bevor er weiter ausgeführt wird und sobald die Antwort an den Client gesendet wurde.

    Sollte also ein Request abgebrochen werden erscheint in der Log nur ein Eintrag.

    Fazit

    Combinedio erlaubt durch die zusätzlichen Werte für Input und Output die beste Auswertung des entstandenen Traffics, da bei Upload-Lastige Seiten wie z.b. ein paar meiner Tools, die Datei-Uploads einen großen Teil des Traffics ausmachen oder auch wenn ein Vhost z.b. mit USVN zum hosten von Subversion-Repositorys via https + webdav genutzt wird. Mit common und combined erhält man hier nur die Größe der abgerufenen Dokumente was teilweise wirklich nur ein Bruchteil des entstandenen Traffics ist.

    Forensic hingegen macht Sinn wenn es sich um eine Sicherheitskritische Applikation handelt und Informationen über auftretende fehlerhafte Requests wichtig sind.

    Tools zur Analyse von combinedio-Logs

    Während sich common und combined vom standard-webalizer auswerten lassen, gilt das nicht für combinedio-Logs. Dazu muss man einen der Forks wie z.b. Webalizer Xtended von Patrick Frei oder Stone Steps Webalizer nutzen. Desweiteren kann auch Awstats mit combinedio-Logfiles umgehen.

    Weitere Logs

    Weitere Logs für andere Bereiche auf die ich hier nicht groß eingehe bietet der Apache Webserver übringends noch für Fehlermeldungen, mod_rewrite und mod_cgi. Bei den Fehlermeldungen lässt sich mit LogLevel einstellen welche Meldungen geloggt werden, bei mod_rewrite ist es über die Direktive RewriteLog und RewriteLogLevel möglich jede Rewrite-Aktion einer URL zu loggen und bei mod_cgi ist es über die ScriptLog Direktive möglich sämtliche Header und die Ausgabe jedes Aufruf eines cgi-Scriptes zu loggen.

    ]]>
    Fri, 16 Oct 2009 21:04:55 +0000
    <![CDATA[Die PHP ZipArchive-Erweiterung und der comp_method-Wert]]> http://www.robo47.net/text/40-Die-PHP-ZipArchive-Erweiterung-und-der-comp_method-Wert http://www.robo47.net/text/40-Die-PHP-ZipArchive-Erweiterung-und-der-comp_method-Wert Also ich meinen Codeschnipsel zum Anzeigen von Informationen über Zip-Archive geschrieben hab, bin auf die Information comp_method aufmerksam geworden die man sich über die Methode statIndex() bzw. statName() des ZipArchiv-Objektes für Dateien ausgeben lassen kann. Dieses Feld gibt an in welcher Form (mit welcher Methode) die einzelne Datei innerhalb des Archivs komprimiert ist. Mögliche Modis lassen sich aus den ZipArchive::CM_-Konstanten und den Angaben im Manual ableiten:

    ZIPARCHIVE::CM_STORE (integer)
    stored (uncompressed).
    ZIPARCHIVE::CM_SHRINK (integer)
    shrunk
    ZIPARCHIVE::CM_REDUCE_1 (integer)
    reduced with factor 1
    ZIPARCHIVE::CM_REDUCE_2 (integer)
    reduced with factor 2
    ZIPARCHIVE::CM_REDUCE_3 (integer)
    reduced with factor 3
    ZIPARCHIVE::CM_REDUCE_4 (integer)
    reduced with factor 4
    ZIPARCHIVE::CM_IMPLODE (integer)
    imploded
    ZIPARCHIVE::CM_DEFLATE (integer)
    deflated
    ZIPARCHIVE::CM_DEFLATE64 (integer)
    deflate64
    ZIPARCHIVE::CM_PKWARE_IMPLODE (integer)
    PKWARE imploding
    ZIPARCHIVE::CM_BZIP2 (integer)
    BZIP2 algorithm

    Leider unterstützt die Zip-Extension diese Werte nur beim lesen, es ist noch nicht möglich diese Werte zu ändern oder beim erstellen eines Archivs zu setzen um.

    Sinnvolle Anwendungsgebiete würden mir da auch ein paar einfallen, z.b. wenn man Archive mit vielen eh schon komprimierten Dateien (jpeg, png, mp3, ... ) erstellen will und den unnötigen Rechenaufwand diese weiter zu komprimieren sparen will könnte man mit ZIPARCHIE::CM_STORE veranlassen dass die Datei(en) überhaupt nicht komprimiert werden und so das Zip-Archiv als einfachen Container genutzt wird. Mit bzip2 wäre es möglich in einigen Fällen eine bessere Kompression auf Kosten einer höheren Rechenzeit zu erreichen.

    Ich hoffe allerdings dass diese Funktionalität es irgendwann in die Zip-Erweiterung schafft.

    ]]>
    Wed, 14 Oct 2009 22:06:39 +0000
    <![CDATA[Web-Gui zur Verwaltung von Subversion Repositorys inklusive Usern, Gruppen und Ordnerbasierten Rechte mit USVN]]> http://www.robo47.net/blog/174-Web-Gui-zur-Verwaltung-von-Subversion-Repositorys-inklusive-Usern-Gruppen-und-Ordnerbasierten-Rechte-mit-USVN http://www.robo47.net/blog/174-Web-Gui-zur-Verwaltung-von-Subversion-Repositorys-inklusive-Usern-Gruppen-und-Ordnerbasierten-Rechte-mit-USVN Wer mal vor dem Problem steht SVN-Repros auf einem Server komfortabel zu verwalten (neue anlegen, alte löschen, Benutzer und Gruppen-Rechte, Ordner-basierte Rechte, Viewer, etc) und nicht immer für jedes Repository sich auf dem Server einloggen will, es händisch anlegen und händisch Log-Dateien für Benutzer und Gruppen editieren will, der sollte sich unbedingt mal USVN anschauen.

    USVN ist ein auf das Zend Framework aufbauende Web-GUI das die Verwaltung von Repositorys, Gruppen und Usern sehr stark erleichtert.

    USVN schreibt dabei normale Configs für SVN die dann im Zusammenspiel mit mod_dav und mod_svn den Usern die Repros via http oder https zur Verfügung stellen.

    USVN existiert momentan in Version 1.0.1 und steht unter der Cecill Lizenz einer zur GPL kompatiblen, französischen Lizenz. Ein paar schöne Screenshots von USVN gibt es hier. Und runterladen kann man es hier.

    ]]>
    Wed, 14 Oct 2009 22:00:37 +0000
    <![CDATA[Exif und IPTC Informationen im Firefox Browser anzeigen mit FxIF und Exif Viewer]]> http://www.robo47.net/text/39-Exif-und-IPTC-Informationen-im-Firefox-Browser-anzeigen-mit-FxIF-und-Exif-Viewer http://www.robo47.net/text/39-Exif-und-IPTC-Informationen-im-Firefox-Browser-anzeigen-mit-FxIF-und-Exif-Viewer Also Hobby-Photograph bin ich oftmals interessiert was für Exif-Daten gewisse Bilder in Foren, auf Blogs oder sonstigen Webseiten haben. Sie immer runterzuladen und lokal mit einem Programm zu öffnen ist auf Dauer viel zu umständlich, also bietet es sich an mit einer Extension direkt im Browser die Informationen anzuzeigen.

    Die Auswahl ist dabei nicht sonderlich groß:

    FxIF ist schön klein und integriert sich im Rechtsklick-Menü in dem Punkt "Eigenschaften", während Exif Viewer einen eigenen Punkt im Rechtsklick-Menü "Zeige Exif-Daten" hinzufügt und dort in Listen oder Tabellen-Form eine Übersicht über die Exif und IPTC-Informationen anzeigt.

    ]]>
    Fri, 09 Oct 2009 10:16:18 +0000
    <![CDATA[Remove Trailing Spaces sed-basierter Ant Task]]> http://www.robo47.net/codeschnipsel/29-Remove-Trailing-Spaces-sed-basierter-Ant-Task http://www.robo47.net/codeschnipsel/29-Remove-Trailing-Spaces-sed-basierter-Ant-Task
    <?xml version="1.0" encoding="UTF-8"?>
    <project name="projectname" basedir=".">
        <target name="removeTrailingSpaces">
            <apply executable   = "sed"
                   failonerror  = "true">
                <arg value="s/[ ]*$//" />
                <arg value="-i" />
                <fileset dir="directory">
                    <include name="**/*.php" />
                    <include name="**/*.phtml" />
                </fileset>
            </apply>
        </target>
    </project>
    ]]>
    Wed, 30 Sep 2009 04:18:13 +0000
    <![CDATA[Die SPL - Standard PHP Library]]> http://www.robo47.net/blog/173-Die-SPL-Standard-PHP-Library http://www.robo47.net/blog/173-Die-SPL-Standard-PHP-Library Seit nun 5 Jahren (seit Version 5.0 von PHP) gibt es die SPL - Standard PHP Library.
    Die an C++'s STL - Standard Template Library angelehnte Extension hatte ihren Ursprung vor 6 Jahren in der PECL - PHP Extension Community Library und wurde in PHP 5 direkt zu einer Core-Komponente die für mich heute kaum noch wegzudenken ist.
    Mit der SPL kamen vordefinierte Interfaces wie Countable, IteratorAggregate und Serializable, die wirklich vielen interessanten Iteratoren, die Klassen SplFileInfo und SplFileObject, ArrayAccess, das es einem endlich ermöglichte eigene Container zu schreiben die den Array-Operator ([]) nutzen und mit PHP 5.3 auch noch ein paar neue Datenstrukturen.

    Inhaltsverzeichnis

    Exceptions

    Die SPL bringt zusätzlich zu der auch seit PHP 5 vorhandenen Standard-Exception einen Satz an weiteren Exceptions für verschiedene Fälle mit die man benutzen kann oder auch darauf seine eigenen Exceptions aufbauen kann. Die Exceptions selbst bieten keinerlei zusätzliche Funktionalität sondern erben nur von der Standard-Exception, sollen aber durch ihre Namen zur Verbesserung der Codequalität beitragen und eine Hilfe beim Debuggen bieten. Ihre Namen ähneln denen aus aus anderen Sprachen wie Java oder C++.

    • BadFunctionCallException
    • BadMethodCallException
    • DomainException
    • InvalidArgumentException
    • LengthException
    • LogicException
    • OutOfBoundsException
    • OutOfRangeException
    • OverflowException
    • RangeException
    • RuntimeException
    • UnderflowException
    • UnexpectedValueException

    Iteratoren

    • AppendIterator (seit PHP 5.1)
    • ArrayIterator (seit PHP 5.0)
    • CachingIterator (seit PHP 5.0)
    • DirectoryIterator (seit PHP 5.0)
    • EmptyIterator (seit PHP 5.1)
    • FilterIterator (seit PHP 5.0)
    • GlobIterator (seit PHP 5.3)
    • InfiniteIterator (seit PHP 5.1)
    • IteratorIterator (seit PHP 5.1)
    • LimitIterator (seit PHP 5.0)
    • MultipleIterator (seit PHP 5.3)
    • NoRewindIterator (seit PHP 5.1)
    • ParentIterator (seit PHP 5.1)
    • RecursiveArrayIterator (seit PHP 5.1)
    • RecursiveCachingIterator (seit PHP 5.1)
    • RecursiveDirectoryIterator (seit PHP 5.0)
    • RecursiveFilterIterator (seit PHP 5.1)
    • RecursiveFilesystemIterator (seit PHP 5.3)
    • RecursiveIteratorIterator (seit PHP 5.0)
    • RecursiveTreeIterator (seit PHP 5.3)
    • RecursiveRegexIterator (seit PHP 5.1)
    • RegexIterator (seit PHP 5.1)
    • SimpleXMLIterator (seit PHP 5.0)

    Die Iteratoren haben wohl bei mir persönlich das größte Interesse geweckt, die Möglichkeiten mit dem DirectoryIterator oder dem RecursiveDirectoryIterator Verzeichnis-Strukturen vernünftig iterieren zu können anstatt umständlich mit glob() oder open/read/closedir() zu arbeiten, kombiniert mit eigenen FilterIterator und SortIterator lässt sich damit sehr komfortabel arbeiten.
    Das SplFileInfo-Objekt erlaubt es beim Traversieren der Verzeichnis-Iteratoren dann auch sehr komfortabel auf alle relevanten Informationen des Elements zuzugreifen und mittels dem SplFileObject lässt sich über die einzelnen Zeilen einer Datei iterieren

    Vordefinierte Interfaces

    Die vordefinierten Interfaces wie Countable, Serializeable, Iterator, IteratorAggregate, RecursiveIterator, Seekableiterator und ArrayAccess spielen eine ganz besondere Rolle, weil sie im Gegensatz zu selbst definierten Interfaces direkt in C geschrieben sind und im Hintergrund die Verknüpfung mit nativen PHP-Funktionen und Sprachkonstrukten (count(), serialize(), foreach()) ermöglichen oder Klassen die das Interface implementieren um Operatoren ([]) erweitern.

    Datenstrukturen

    Seit PHP 5.3 sind auch einige Klassen dazugekommen die dynamische und Geschwindigkeits/Speicherverbrauch-optimierte Datenstrukturen zur Verfügung stellen.
    Dazu gehören:
    • SplDoublyLinkedList
    • SplStack
    • SplQueue
    • SplHeap
    • SplMaxHeap
    • SplMinHeap
    • SplPriorityQueue
    • SplFixedArray
    • SplObjectStorage

    Eine nette Presentation mit ein paar Benchmarks zu diesen Datenstrukturen von Matthew Turland findet man bei Slideshare: New SPL Features in PHP 5.4

    Observer

    Mit den Interfaces SplObserver und SplSubject stellt die SPL seit PHP 5.1 auch fertige Beispiel-Interfaces für das Observer-Pattern zur Verfügung. Zur Implementierung des Observers bietet sich dann gleich die neue SplObjectStorage-Klasse an.

    Funktionen

    Auch wenn die SPL eigentlich eine wachsende Sammlung von (abstrakten) Klassen und Interfaces ist, haben sich auch ein paar Funktionen dazugesellt.
    • class_implements - Gibt die implementierten Interfaces einer Klasse zurück
    • class_parents - Gibt die Elternklassen eines Objekts zurück
    • iterator_apply - Wendet eine Callback-Funktion auf alle Elemente eines Interators an (wie array_walk für Arrays)
    • iterator_count - Zählt die Anzahl der Elemente eines Iterators
    • iterator_to_array - Wandelt einen Iterator in ein Array mit Elementen um
    • spl_autoload_call - Funktion zum laden einer Klasse mit den vorhandenen Autoload-Funktionen
    • spl_autoload_extensions - Gibt die verwendeten Dateiendungen für die Autoload-Funktion zurück
    • spl_autoload_functions - Gibt alle vorhandenen Autoload-Funktionen zurück
    • spl_autoload_register - Hinzufügen von Autoload-Funktionen
    • spl_autoload_unregister - Entfernen von Autoload-Funktionen
    • spl_autoload - Implementierung für die autoload-Funktion
    • spl_classes - Gibt ein Array mit allen in der aktuellen Version zur Verfügung gestellten SPL-Klassen zurück
    • spl_object_hash - Gibt einen eindeutigen Hash für das Objekt zurück

    Sehr interessant sind die spl_autoload_*-Funktionen die in den Autoloadern der meisten Frameworks und Bibliotheken genutzt werden, damit lassen sich sehr schöne Regeln zum automatischen Laden (Lazy Load) von Klassen und Interfaces erstellen damit nur Klassen geladen werden die auch wirklich gebraucht werden, aber jeder gleichzeitig frei ist das Mapping zwischen Klassenname und Dateinamen selbst zu bestimmen.
    So lassen sich auch verschiedene Frameworks und Bibliothken mit Autoloading zusammen nutzen. Was man schön am Autoloader des Zend Frameworks sieht.

    SPL-Codeschnipsel

    Auch ich habe ein paar Codeschnipsel auf meiner Seite die Klassen und Interfaces der SPL nutzen, insbesondere DirectoryIterator, RecursiveDirectoryIterator und SplFileInfo.

    Weiterführende Informationen

    Meiner Meinung nach die beste und aktuellste API-Übersicht über die SPL von Marcus "helly" Börger findet man hier auf php.net: http://www.php.net/~helly/php/ext/spl/.

    Zu beachten ist, dass es dort auch Iteratoren, Interfaces und Klassen gibt die es noch nicht in ein Releases der SPL geschafft haben, manche sind noch in der Entwicklung und werden wohl in späteren Releases von PHP 5.3 zur Verfügung stehen.

    Ansonsten gibt es natürlich auch im PHP-Manual ein paar Seiten über die SPL, diese sind allerdings meistens nicht auf dem neuesten Stand.

    Das Buch Professionelle Softwareentwicklung mit PHP 5 von Sebastian Bergmann (Programmierer von PHPUnit) bietet neben anderem auch einen guten Einstieg in die SPL mit ein paar Code-Beispielen. Das Buch gibt es kostenlos zum online lesen unter http://professionelle-softwareentwicklung-mit-php5.de/ oder bei Amazon: Professionelle Softwareentwicklung mit PHP 5: Objektorientierung, Entwurfsmuster, Modellierung und fortgeschrittene Datenbankprogrammierung

    Fazit

    Obwohl die SPL einiges zu bieten hat, sie auch schon in einigen Frameworks (wie dem Zend Framework oder Symfony) zum Einsatz kommt, wird sie leider von vielen PHP-Programmierer nicht genutzt, was meiner Meinung nach am stärksten an ihrem geringen Bekanntheitsgrad liegt und auch etwas an der Dokumentation (und den Übersetzungen) im PHP-Manual die meistens sehr stark hinter der wirklichen Entwicklung hinterherhinken. Trotzdem kann ich jedem Programmierer der mit PHP 5 oder sogar 5.3 arbeitet nur empfehlen sich diese Extension mal genauer anzuschauen. Ich werde versuchen in der nächsten Zeit noch ein paar mehr Codeschnipsel und vielleicht kleine Artikel zur SPL zu verfassen um die Möglichkeiten die sich insbesondere durch die Iteratoren ergeben aufzeigen.

    ]]>
    Tue, 29 Sep 2009 16:09:23 +0000
    <![CDATA[Größen und Dateityp gefilterte Dateiliste mit dem RecursiveDirectoryIterator, dem RegexIterator und einem eigenen FilterIterator (SizeFilterIterator)]]> http://www.robo47.net/codeschnipsel/28-Groesen-und-Dateityp-gefilterte-Dateiliste-mit-dem-RecursiveDirectoryIterator-dem-RegexIterator-und-einem-eigenen-FilterIterator-SizeFilterIterator http://www.robo47.net/codeschnipsel/28-Groesen-und-Dateityp-gefilterte-Dateiliste-mit-dem-RecursiveDirectoryIterator-dem-RegexIterator-und-einem-eigenen-FilterIterator-SizeFilterIterator Ausserdem kommt danach noch der RegexFilter (auch ein FilterIterator, aber Bestandteil der SPL) zum Einsatz um nur die .pdf und .txt Dateien auszugeben.

    <pre>
    <?php
    $dirIter = new RecursiveDirectoryIterator('./');
    $recursiveIterator = new RecursiveIteratorIterator($dirIter,
        RecursiveIteratorIterator::SELF_FIRST,
        RecursiveIteratorIterator::CATCH_GET_CHILD);
    
    /**
     * Filter
     */
    class mySizeFilterIterator extends FilterIterator
    {
        protected $isMinSize = true;
        protected $size = 0;
    
        /**
         *
         * @param Iterator
         * @param integer
         * @param boolean
         */
        public function __construct(Iterator $iterator,
                                    $size,
                                    $minSize = true)
        {
            parent::__construct($iterator);
            $this->size = $size;
            $this->isMinSize = $minSize;
        }
    
        /**
         * Filtert ungewünschte Einträge heraus
         * @throws InvalidArgumentException
         * @return bool
         */
        public function accept() {
            if (!parent::current() instanceof SplFileInfo) {
                $message = 'Iterators value need to be a SplFileInfo-Object';
                throw new InvalidArgumentException($message);
            }
            if ($this->isMinSize) {
                if (parent::current()->getSize() <= $this->size) {
                    return false;
                }
            } else {
                if (parent::current()->getSize() >= $this->size) {
                    return false;
                }
            }
            return true;
        }
    }
    
    $sizeIterator = new mySizeFilterIterator($recursiveIterator, 300, true);
    
    foreach($sizeIterator as $key => $value) {
        // $key ist der alte Key
        // $value weiterhin das SplFileInfo-Objekt
        echo $key . PHP_EOL;
    }
    echo '<hr />';
    
    // Jetzt erweitern wir das ganze noch mit dem RegexIterator um nur
    // pdf und txt-Dateien zu bekommen
    $regexIterator = new RegexIterator($sizeIterator,
                                       '~^.+\.(pdf|txt)$~i');
    
    foreach($regexIterator as $key => $value) {
        // $key ist der alte Key
        // $value weiterhin das SplFileInfo-Objekt
        echo $key . PHP_EOL;
    }
    
    ?>
    </pre>
    ]]>
    Tue, 29 Sep 2009 11:18:24 +0000
    <![CDATA[Alle Bilder (jpg, jpeg, png, gif) aus einem Verzeichnis mit PHP und dem RegexIterator herausfiltern]]> http://www.robo47.net/codeschnipsel/27-Alle-Bilder-jpg-jpeg-png-gif-aus-einem-Verzeichnis-mit-PHP-und-dem-RegexIterator-herausfiltern http://www.robo47.net/codeschnipsel/27-Alle-Bilder-jpg-jpeg-png-gif-aus-einem-Verzeichnis-mit-PHP-und-dem-RegexIterator-herausfiltern
    <pre>
    <?php
    $dirIter = new RecursiveDirectoryIterator('./');
    $recursiveIterator = new RecursiveIteratorIterator($dirIter,
        RecursiveIteratorIterator::SELF_FIRST,
        RecursiveIteratorIterator::CATCH_GET_CHILD);
    $regexIterator = new RegexIterator($recursiveIterator,
                                       '~^.+\.(jpeg|jpg|gif|png)$~i',
                                       RegexIterator::GET_MATCH);
    
    foreach($regexIterator as $key => $value) {
        // $key ist jetzt der alte Key
        // $value ein Array mit matches genauso wie $matches bei  
        // http://de2.php.net/preg_match
        echo $key . PHP_EOL;
    }
    ?>
    </pre>
    ]]>
    Tue, 29 Sep 2009 10:48:03 +0000
    <![CDATA[Mail() ist tot, es lebe mail()]]> http://www.robo47.net/text/38-Mail-ist-tot-es-lebe-mail http://www.robo47.net/text/38-Mail-ist-tot-es-lebe-mail Immer wieder sieht man wie sich Leute mit mail() abquälen wenn sie mit PHP E-Mails versenden wollen.

    Die Probleme können hier verschiedene Ursachen haben:

    • unzureichende Kenntnisse über relevante RFC's (Umbrüche, Datumsformate)
    • fehlende/falsche header
    • bescheidenes/umständliches Setup beim Hoster (Zusätzliches freischalten von Absender, Limitierungen für Anzahl, Größe, Empfängerzahl ... )

    Während es für die kleine schnelle Info-Mail gerade noch okay ist, versuchen Leute immer wieder das Rad neu zu erfinden wenn es um Dinge wie HTML-Mails, Datei-Anhängen, eingebette Daten oder die passende Mail-Header geht.
    Das muss heutzutage absolut nicht sein, denn dieses Rad wurde schon zu oft neu erfunden und die Chance dass E-Mails wegen Kleinigkeiten (falscher Umbruch, ungültiger Header, falsches Datumsformat, ...) von anderen Mailservern als SPAM markiert werden, ganz abgelehnt werden oder der Mailclient sie am Ende nicht ordentlich darstellen kann ist einfach zu groß.
    Die Möglichkeiten eventuelle Fehler zu debuggen sind oftmals sehr schwer, weil sie von vielen Faktoren wie z.b. dem eigenen Mailserver, dem fremden Mailserver, dem Mailclient des Empfängers, der jeweiligen Konfiguration dieser Komponenten und eventueller weiterer Komponenten abhängt.
    Deshalb sollte man zum verschicken von Emails auf fertige erprobte und geteste Klassen zurückgreifen.

    Hier gibt es mittlerweile einige sehr gute Klassen die neben HTML-Mails, eingebetten Daten (CSS, Bilder), Datei-Anhängen und Mail-Headern auch noch verschiedene Transports (mail(), Sendmail und SMTP) unterstützen. Ausserdem verfügen manche sogar noch über Plugins oder Erweiterungen für LoadBalancing zwischen mehreren Servern, Failover, Speicher-sparende Versand (trotz großer Dateianhänge) und Queueing die sich beim Massenversand von Mails von Vorteil sein können.

    Beim Versand über SMTP spart man sich oftmals auch die Nervereien mit dem umständlichen Setup beim Hoster und gerade beim Versenden von großen Mengen an Mails (Newsletter etc) ist der direkte Versand via SMTP auch noch schneller.

    Ein paar dieser Klassen möchte ich hier mal auflisten und für ein paar habe ich auch ein paar fertige Codeschnipsel die zeigen wie man damit eine HTML-Email via SMTP versendet.

    Mailer Version PHP-Version Extensions Lizenz Links
               
    phpmailer 5.0.2 (26.05.2009) php 5   LGPL v2.1 Download
    Zend_Mail 1.9.3 (22.09.2009) php 5.2 spl
    optional: posix
    New BSD License Download
    ezcMail 1.6.3 (22.06.2009)
    ezComponents 2009.1.2
    php 5.2 pcre, spl, reflection, iconv
    optional: openssl, mcrypt, fileinfo
    New BSD License Download
    Dokumentation

    API
    Swiftmailer 4.0.5 (27.09.2009) php5 spl LGPL 3 Download
    Dokumentation
    Swiftmailer 3.3.3 (26.03.2008) php 4/php5   LGPL 3 Download
    Dokumentation
    PEAR Mail
    (PEAR Mime)
    1.1.4 (11.10.2006)
    1.2.0 beta (15.5.2009)
    php4   PHP 2.02/New BSD License Download
    Dokumentation

    Alle diese Klassen bieten die Grundfunktionen für den Versand von (Html-)E-Mails via mail() oder SMTP, erlauben es Dateianhänge hinzuzufügen und unterstützen das setzen von Mail-Headern (Return-Path, etc).

    Ich selbst setzte meistens Zend_Mail ein da ich eh mit dem Zend_Framework arbeite. Ansonsten fällt meine Wahl auf den Swiftmailer.

    Codeschnipsel für den Versand von HTML-Emails via SMTP:

    ]]>
    Mon, 28 Sep 2009 01:43:34 +0000
    <![CDATA[Größe der gemounteten Festplatte/Laufwerke auslesen mit PHP unter Linux]]> http://www.robo47.net/codeschnipsel/26-Groese-der-gemounteten-Festplatte-Laufwerke-auslesen-mit-PHP-unter-Linux http://www.robo47.net/codeschnipsel/26-Groese-der-gemounteten-Festplatte-Laufwerke-auslesen-mit-PHP-unter-Linux Beispiel-Ausgabe:
    Es wurden 2 Laufwerke gefunden: 
    
    Laufwerk: /dev/md2
    Dateisystem: ext3
    Size: 688G
    Belegt: 36G
    Frei: 617G
    Benutzt %: 6%
    Mountpunkt: /
    
    Laufwerk: /dev/md1
    Dateisystem: ext3
    Größe: 2.0G
    Belegt: 86M
    Frei: 1.9G
    Benutzt %: 5%
    Mountpunkt: /boot
    
    
    


    <pre>
    <?php
    exec('df -h -T', $output);
    $devices = array();
    foreach($output as $line) {
        $expl = explode(' ', preg_replace('~[ ]+~', ' ', $line));
        // ignore all drives which don't begin with /
        if (count($expl) == 7 && strlen($expl[0]) > 0 && $expl[0]{0} == '/') {
            $devices[] = array('device' => $expl[0],
                               'filesystem' => $expl[1],
                               'size' => $expl[2],
                               'used' => $expl[3],
                               'free' => $expl[4],
                               'usedpercent' => $expl[5],
                               'mountpoint' => $expl[6]);
        }
    }
    echo 'Es wurden ' . count($devices) . ' Laufwerke gefunden: ' . PHP_EOL .
         PHP_EOL;
    foreach($devices as $device) {
        echo 'Laufwerk: ' . $device['device'] . PHP_EOL;
        echo 'Dateisystem: ' . $device['filesystem'] . PHP_EOL;
        echo 'Size: ' . $device['size'] . PHP_EOL;
        echo 'Belegt: ' . $device['used'] . PHP_EOL;
        echo 'Frei: ' . $device['free'] . PHP_EOL;
        echo 'Benutzt %: ' . $device['usedpercent'] . PHP_EOL;
        echo 'Mountpunkt: ' . $device['mountpoint'] . PHP_EOL;
        echo PHP_EOL;
    }
    ?>
    </pre>
    
    ]]>
    Sun, 27 Sep 2009 14:57:16 +0000
    <![CDATA[Prozessor / CPU mit PHP unter Linux auslesen]]> http://www.robo47.net/codeschnipsel/25-Prozessor-CPU-mit-PHP-unter-Linux-auslesen http://www.robo47.net/codeschnipsel/25-Prozessor-CPU-mit-PHP-unter-Linux-auslesen Beispiel-Ausgabe:
    Das System hat 8 Prozessoren und/oder Kerne:
     - Intel(R) Core(TM) i7 CPU         920  @ 2.67GHz
     - Intel(R) Core(TM) i7 CPU         920  @ 2.67GHz
     - Intel(R) Core(TM) i7 CPU         920  @ 2.67GHz
     - Intel(R) Core(TM) i7 CPU         920  @ 2.67GHz
     - Intel(R) Core(TM) i7 CPU         920  @ 2.67GHz
     - Intel(R) Core(TM) i7 CPU         920  @ 2.67GHz
     - Intel(R) Core(TM) i7 CPU         920  @ 2.67GHz
     - Intel(R) Core(TM) i7 CPU         920  @ 2.67GHz
    


    <pre>
    <?php
    exec('cat /proc/cpuinfo', $output);
    $processors = array();
    foreach($output as $line) {
        if (strpos($line, 'model name') !== false) {
            list($null, $cpu) = explode(':', $line);
            $processors[] = trim($cpu);
        }
    }
    echo 'Das System hat ' . count($processors) . ' Prozessoren und/oder Kerne:' .
         PHP_EOL;
    foreach($processors as $processor) {
        echo ' - ' . $processor . PHP_EOL;
    }
    ?>
    </pre>
    ]]>
    Sun, 27 Sep 2009 14:34:34 +0000
    <![CDATA[Größe des Arbeitsspeichers und der Swap-Datei mit php unter Linux bestimmen]]> http://www.robo47.net/codeschnipsel/24-Groese-des-Arbeitsspeichers-und-der-Swap-Datei-mit-php-unter-Linux-bestimmen http://www.robo47.net/codeschnipsel/24-Groese-des-Arbeitsspeichers-und-der-Swap-Datei-mit-php-unter-Linux-bestimmen
    <pre>
    <?php
    // -b -> Speicher in Bytes
    // -k -> Speicher in Kilobyte
    // -m -> Speicher in Megabyte
    // -g -> Speicher in Gigabytes
    exec('free -t -m', $output);
    list($null, $ram_total, $ram_used, $ram_free, $ram_shared, $ram_buffers, 
         $ram_cached)
         = explode(' ', preg_replace('~[ ]+~', ' ', $output[1]));
    list($null, $swap_total, $swap_used, $swap_free)
        = explode(' ', preg_replace('~[ ]+~', ' ', $output[3]));
    list($null, $total_total, $total_used, $total_free)
         = explode(' ', preg_replace('~[ ]+~', ' ', $output[4]));
    unset($null);
    echo 'Arbeitsspeicher: ' . $ram_total . 'MB' . PHP_EOL;
    echo 'benutzt: ' . $ram_used . 'MB' .PHP_EOL;
    echo 'frei: ' . $ram_free . 'MB' .PHP_EOL;
    echo PHP_EOL;
    echo 'Swap: ' . $swap_total . 'MB' .PHP_EOL;
    echo 'benutzt: ' . $swap_used . 'MB' .PHP_EOL;
    echo 'frei: ' . $swap_free . 'MB' .PHP_EOL;
    echo PHP_EOL;
    echo 'Total: ' . $total_total . 'MB' .PHP_EOL;
    echo 'benutzt: ' . $total_used . 'MB' .PHP_EOL;
    echo 'frei: ' . $total_free . 'MB' .PHP_EOL;
    ?>
    </pre>
    ]]>
    Sun, 27 Sep 2009 14:14:15 +0000
    <![CDATA[PHP Syntax-Check Lint Ant Task]]> http://www.robo47.net/codeschnipsel/23-PHP-Syntax-Check-Lint-Ant-Task http://www.robo47.net/codeschnipsel/23-PHP-Syntax-Check-Lint-Ant-Task
    <?xml version="1.0" encoding="UTF-8"?>
    <project name="projectname" basedir=".">
        <target name="phplinttest">
            <apply executable   = "php"
                   failonerror  = "true"
                   error        = "checkphp.err.txt">
                <arg value="-l" />
                <fileset dir="application">
                    <include name="**/*.php" />
                    <include name="**/*.phtml" />
                </fileset>
                <fileset dir="scripts">
                    <include name="**/*.php" />
                </fileset>
                <fileset dir="public">
                    <include name="**/*.php" />
                </fileset>
                <fileset dir="library/App">
                    <include name="**/*.php" />
                </fileset>
                <fileset dir="tests">
                    <include name="**/*.php" />
                </fileset>
            </apply>
        </target>
    </project>
    ]]>
    Wed, 23 Sep 2009 17:36:41 +0000
    <![CDATA[Canon Objektive und die darin verbauten Bildstabilisator Generationen]]> http://www.robo47.net/text/37-Canon-Objektive-und-die-darin-verbauten-Bildstabilisator-Generationen http://www.robo47.net/text/37-Canon-Objektive-und-die-darin-verbauten-Bildstabilisator-Generationen Objektiv Blenden vom 3-Bein Stativ nutzbar Modis Anlaufzeit Erscheinungs Jahr             Canon EF-S 15-85mm 1:3.5-5.6 IS USM 1-4 ja Automatischer Wechsel 0,5s Oktober 2009 Canon EF-S 17-55mm 1:2.8L IS USM 1-3 ja - 0,5s Mai 2006 Canon EF-S 17-85mm 1:4-5.6 IS USM 1-3 ja - 0,5s September 2004 Canon EF-S 18-55mm 1:3.5-5.6 IS 1-4 ja Automatischer Wechsel 0,5s September 2007 Canon EF-S 18-135mm 1:3.5-5.6 IS 1-4 ja Automatischer Wechsel 0,5s Oktober 2009 Canon EF-S 18-200mm 1:3.5-5.6 IS 1-3 ja - 0,5s September 2008 Canon EF-S 55-250mm 1:4-5.6 IS 1-4 ja Automatischer Wechsel 0,5s November 2007 Canon EF 24-105mm 1:4L IS USM 1-3 ja - 0,5s Oktober 2005 Canon EF 28-135mm 1:3.5-5.6 IS USM 1-2 nein - 1s Februar 1998 Canon EF 28-300mm 1:3.5-5.6L IS USM 1-3 ja IS Mode 1/2 0,5s Juni 2004 Canon EF 70-200mm 1:2.8L IS USM 1-3 ja IS Mode 1/2 0,5 September 2001 Canon EF 70-200mm 1:4L IS USM 1-4 ja IS Mode 1/2 0,5 November 2006 Canon EF 70-300mm 1:4-5.6 IS USM 1-3 nein IS Mode 1/2 0,5s Oktober 2005 Canon EF 70-300mm 1:4.5-5.6 DO IS USM 1-3 nein IS Mode 1/2 0,5s Juni 2004 Canon EF 75-300mm 1:4.5-5.6 IS USM 1-2 nein   1s September 1995 Canon EF 100mm 1:2.8L Macro IS USM 1-2 bei 1facher Vergrößerung
    1-3 bei 0,5facher Vergrößerung
    1-4 bei normalen Aufnahmen ja Automatischer Wechsel 0,5s September 2004 Canon EF 100-400mm 1:4.5-5.6L IS USM 1-2 nein IS Mode 1/2 1s November 1998 Canon EF 200mm 1:2L IS USM 1-5 ja IS Mode 1/2 0,5s April 2008 Canon EF 300mm 1:4L IS USM 1-2 nein IS Mode 1/2 1s März 1997 Canon EF 300mm 1:2.8L IS USM 1-2 ja IS Mode 1/2 1s Juli 1999 Canon EF 400mm 1:2.8L IS USM 1-2 ja IS Mode 1/2 1s September 1999 Canon EF 400mm 1:4 DO IS USM 1-3 ja IS Mode 1/2 0,5 Dezember 2001 Canon EF 500mm 1:4L IS USM 1-2 ja IS Mode 1/2 1s Juli 1999 Canon EF 600mm 1:4L IS USM 1-2 ja IS Mode 1/2 1s September 1999 Canon EF 800mm 1:5.6L IS USM 1-4 ja IS Mode 1/2 0,5s Mai 2008


    Quellen unter anderem:

    IS Modes

    IS Mode 1 korrigiert Verwackeln in allen Richtungen, wohingegen IS Mode 2 nur eine Richtung korrigiert um z.b. auch bei Mitziehern genutzt zu werden.

    Ein paar weitere Informationen zum Bildstabilisator von Canon finden sich auch auf der Webseite von Fritz Pölking

    Sollten Werte oder Angaben nicht stimmen würde ich mich über einen Kommentar, eine Email oder eine Nachricht übers Kontaktformular freuen.

    ]]>
    Wed, 23 Sep 2009 16:22:27 +0000
    <![CDATA[CSV-Dateiliste eines Ordners erstellen]]> http://www.robo47.net/codeschnipsel/21-CSV-Dateiliste-eines-Ordners-erstellen http://www.robo47.net/codeschnipsel/21-CSV-Dateiliste-eines-Ordners-erstellen
    <?php
    error_reporting(E_ALL | E_STRICT);
    $dirIter = new RecursiveDirectoryIterator('./');
    $recursiveIterator = new RecursiveIteratorIterator($dirIter,
        RecursiveIteratorIterator::SELF_FIRST,
        RecursiveIteratorIterator::CATCH_GET_CHILD);
    
    $fp = fopen('file.csv', 'w');
    
    foreach ($recursiveIterator as $element) {
        /* @var $element SplFileInfo */
        $line = array('name' => $element->getPathname(),
                      'type' => $element->getType(),
                      'size' => $element->getSize());
        fputcsv($fp, $line, ',', '"');
    }
    
    fclose($fp);
    
    echo file_get_contents('file.csv');
    ]]>
    Wed, 23 Sep 2009 00:22:20 +0000
    <![CDATA[TXT-Dateiliste eines Ordners erstellen]]> http://www.robo47.net/codeschnipsel/22-TXT-Dateiliste-eines-Ordners-erstellen http://www.robo47.net/codeschnipsel/22-TXT-Dateiliste-eines-Ordners-erstellen
    <?php
    error_reporting(E_ALL | E_STRICT);
    $dirIter = new RecursiveDirectoryIterator('./');
    $recursiveIterator = new RecursiveIteratorIterator($dirIter,
        RecursiveIteratorIterator::SELF_FIRST,
        RecursiveIteratorIterator::CATCH_GET_CHILD);
    
    $fp = fopen('file.txt', 'w');
    
    foreach ($recursiveIterator as $element) {
        /* @var $element SplFileInfo */
        fputs($fp, $element->getPathname() . "\n");
    }
    
    fclose($fp);
    
    echo file_get_contents('file.txt');
    ]]>
    Wed, 23 Sep 2009 00:22:20 +0000
    <![CDATA[Lineare XML-Dateiliste eines Ordner über den RecursiveDirectoryIterator und DOM erstellen]]> http://www.robo47.net/codeschnipsel/20-Lineare-XML-Dateiliste-eines-Ordner-ueber-den-RecursiveDirectoryIterator-und-DOM-erstellen http://www.robo47.net/codeschnipsel/20-Lineare-XML-Dateiliste-eines-Ordner-ueber-den-RecursiveDirectoryIterator-und-DOM-erstellen
    <?php
    error_reporting(E_ALL | E_STRICT);
    $dirIter = new RecursiveDirectoryIterator('./');
    $recursiveIterator = new RecursiveIteratorIterator($dirIter,
        RecursiveIteratorIterator::SELF_FIRST,
        RecursiveIteratorIterator::CATCH_GET_CHILD);
    
    $document = new DOMDocument();
    $list = $document->createElement('list', null);
    $document->appendChild($list);
    
    foreach ($recursiveIterator as $element) {
        /* @var $element SplFileInfo */
        $newNode = $document->createElement('entry', null);
        /* @var $newNode DOMNode */
        $newNode->setAttribute( 'name' , $element->getPathname() );
        $newNode->setAttribute( 'type' , $element->getType() );
        $newNode->setAttribute( 'size' , $element->getSize() );
        $list->appendChild($newNode);
    }
    header('Content-Type: text/xml');
    echo $document->saveXML();
    ]]>
    Wed, 23 Sep 2009 00:13:11 +0000
    <![CDATA[Verschachtelte XML-Dateiliste eines Ordner über den RecursiveDirectoryIterator und DOM erstellen]]> http://www.robo47.net/codeschnipsel/19-Verschachtelte-XML-Dateiliste-eines-Ordner-ueber-den-RecursiveDirectoryIterator-und-DOM-erstellen http://www.robo47.net/codeschnipsel/19-Verschachtelte-XML-Dateiliste-eines-Ordner-ueber-den-RecursiveDirectoryIterator-und-DOM-erstellen
    <?php
    error_reporting(E_ALL | E_STRICT);
    $dirIter = new RecursiveDirectoryIterator('./');
    
    $document = new DOMDocument();
    $list = $document->createElement('list', null);
    $document->appendChild($list);
    
    function addLevel(RecursiveDirectoryIterator $iterator, 
                      DOMNode $parentNode,
                      DOMDocument $document)
    {
        foreach ($iterator as $element) {
            /* @var $element SplFileInfo */
    
            $newNode = $document->createElement('entry', null);
            /* @var $newNode DOMNode */
            $newNode->setAttribute( 'name' , $element->getPathname() );
            $newNode->setAttribute( 'type' , $element->getType() );
            $newNode->setAttribute( 'size' , $element->getSize() );
            $parentNode->appendChild($newNode);
            if ($iterator->hasChildren()) {
                addLevel($iterator->getChildren(), $newNode, $document);
            }
        }
    }
    addLevel($dirIter, $list, $document);
    
    header('Content-Type: text/xml');
    echo $document->saveXML();
    ]]>
    Wed, 23 Sep 2009 00:07:08 +0000
    <![CDATA[Deutsche Wörterbücher für Rechtschreibprüfung in Evolution und Pidgin unter Debian / Ubuntu installieren]]> http://www.robo47.net/text/36-Deutsche-Woerterbuecher-fuer-Rechtschreibpruefung-in-Evolution-und-Pidgin-unter-Debian-Ubuntu-installieren http://www.robo47.net/text/36-Deutsche-Woerterbuecher-fuer-Rechtschreibpruefung-in-Evolution-und-Pidgin-unter-Debian-Ubuntu-installieren Pidgin und Evolution können, wenn installiert, aspell zur Rechtschreibkorrektur nutzen. Um das deutsche Wörter zu installieren reicht ein:

    sudo apt-get install aspell-de

    In Pidgin kann man das hervorheben von falsch geschriebenen Wörter unter Werkzeuge -> Einstellungen -> Unterhaltungen -> Falsch geschriebene Wörter hervorheben aktivieren.
    In Evolution findet man es unter Bearbeiten -> Einstellungen -> Editoreinstellungen -> Rechtschreibprüfung -> Rechtschreibprüfung während der Eingabe durchführen. Dort lässt sich dann auch das gewünschte Wörterbuch auswählen.

    ]]>
    Tue, 22 Sep 2009 23:43:10 +0000
    <![CDATA[Shell in Tilda leeren (clear / reset)]]> http://www.robo47.net/text/35-Shell-in-Tilda-leeren-clear-reset http://www.robo47.net/text/35-Shell-in-Tilda-leeren-clear-reset Wer bevor er zu GNOME gewechselt ist KDE genutzt hat und dort als Drop-Down-Shell (Quake oder First-Person-Shooter-Konsole) Yakuake genutzt hat war es gewohnt dass ein

    clear

    oder die Tastenkombination STRG + L wirklich den kompletten aktuellen Shell-Tab leert hat (auch Scrollbar wird zurückgesetzt und alte Inhalte sind wirklich weg, wie im Gnome-Terminal über Terminal -> Zurücksetzen und leeren), wird bei Tilda feststellen, dass dem nicht so ist. Das hat mich einige Zeit lang gestört bis ich darauf gestoßen bin dass man mittels

    reset

    die komplette Shell zurücksetzen kann, womit auch bei Tilda sämtliche alten Inhalte wieder entfernt werden.

    ]]>
    Tue, 22 Sep 2009 22:04:37 +0000
    <![CDATA[Kleines Update]]> http://www.robo47.net/blog/172-Kleines-Update http://www.robo47.net/blog/172-Kleines-Update Tue, 22 Sep 2009 14:35:37 +0000 <![CDATA[Half-Life / Counter Strike 1.6 server unter 64-Bit Linux (Debian Lenny) installieren]]> http://www.robo47.net/text/34-Half-Life-Counter-Strike-1.6-server-unter-64-Bit-Linux-Debian-Lenny-installieren http://www.robo47.net/text/34-Half-Life-Counter-Strike-1.6-server-unter-64-Bit-Linux-Debian-Lenny-installieren Gestern stand ich vor dem Problem einen Counter-Strike-Server auf dem Hetzner EQ4 Root Server (Intel i7 mit 64bit Debian Lenny) zu installieren. Das Problem war, dass er die runtergeladene hldsupdatetool.bin nicht ausführen wollte. Die Fehlermeldung war recht kryptisch und ergab ohne googlen erstmal keinen Sinn:

    ./hldsupdatetool.bin: No such file or directory.

    Nachdem ich mir absolut sicher war dass die Datei auch wirklich existierte hab ich nach viel googlen dann endlich die Lösung des Problems gefunden:
    Die binary ist eine 32bit-binary was man mit dem Befehl

    file hldsupdatetool.bin

    herausfinden kann.
    Ausgabe:

    hldsupdatetool.bin: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), stripped

    Das Problem mit der 32Bit-binary ist, dass die sich auf einem reinen 64bit system nicht ausführen lässt. Dazu muss man erst die ia32-libs installieren.

    Unter Debian/Ubuntu geht das via:

    apt-get install ia32-libs

    Danach lässt sich die binary problemlos ausführen um den Steam-Client zu entpacken über den man dann den Counter-Strike/Half-Life 1.6 Server installieren kann.

    Mehr zu Installation, Konfiguration und dem starten findet man bei counter-strike.de.

    ]]>
    Tue, 22 Sep 2009 10:36:00 +0000
    <![CDATA[Bin ne Woche weg]]> http://www.robo47.net/blog/171-Bin-ne-Woche-weg http://www.robo47.net/blog/171-Bin-ne-Woche-weg Cadaqués) tauchen. Sollte so am 20. wieder im Lande sein.]]> Fri, 11 Sep 2009 15:24:46 +0000 <![CDATA[Einzelnes Verzeichnis aus Zip-Archiv mit php und ZipArchiv entpacken]]> http://www.robo47.net/codeschnipsel/17-Einzelnes-Verzeichnis-aus-Zip-Archiv-mit-php-und-ZipArchiv-entpacken http://www.robo47.net/codeschnipsel/17-Einzelnes-Verzeichnis-aus-Zip-Archiv-mit-php-und-ZipArchiv-entpacken Dabei bleibt die Verzeichnisstruktur erhalten, inklusive dem Verzeichnis das man entpacken will.

    <?php
    $absolutePath = realpath('tmp/'); // Absoluter Pfad wohin man entpacken will
    $directory = '/archiv'; // Verzeichnis im Archiv das man entpacken will
    $archiv = new ZipArchive();
    $archiv->open('archiv.zip');
    $i = 0;
    $entries = array();
    // Alle Dateien raussuchen deren Pfad mit dem gewünschten Pfad anfängt
    while(false !== ($stat = $archiv->statIndex($i))) {
        if (strpos($stat['name'], $directory ) === 0) {
            echo $stat['name'] . '<br />';
            $entries[] = $stat['name'];
        }
        $i++;
    }
    // nur die gewünschten Einträge entpacken
    // allerdings wird dabei die Archiv-Struktur inklusive $directory beibehalten.
    $archiv->extractTo($absolutePath, $entries); 
    ]]>
    Thu, 20 Aug 2009 12:53:42 +0000
    <![CDATA[Archiv-Statistik mit Größe gepackt und Ungepackt für Zip-Archive mit php und ZipArchive]]> http://www.robo47.net/codeschnipsel/18-Archiv-Statistik-mit-Groese-gepackt-und-Ungepackt-fuer-Zip-Archive-mit-php-und-ZipArchive http://www.robo47.net/codeschnipsel/18-Archiv-Statistik-mit-Groese-gepackt-und-Ungepackt-fuer-Zip-Archive-mit-php-und-ZipArchive
    <?php
    $archivFile = 'archiv.zip';
    $archiv = new ZipArchive();
    $archiv->open($archivFile);
    $i = 0;
    $sizeCompressed = 0;
    $sizeUncompressed = 0;
    
    // Manche Konstanten sind nur definiert wenn sie auch unterstütz werden 
    // (z.b. CM_BZIP2)
    $modes = array();
    $modes[ZIPARCHIVE::CM_DEFAULT] = 'default';
    $modes[ZIPARCHIVE::CM_STORE] = 'store';
    $modes[ZIPARCHIVE::CM_SHRINK] = 'shrink';
    $modes[ZIPARCHIVE::CM_REDUCE_1] = 'reduce 1';
    $modes[ZIPARCHIVE::CM_REDUCE_2] = 'reduce 2';
    $modes[ZIPARCHIVE::CM_REDUCE_3] = 'reduce 3';
    $modes[ZIPARCHIVE::CM_REDUCE_4] = 'reduce 4';
    $modes[ZIPARCHIVE::CM_IMPLODE] = 'implode';
    $modes[ZIPARCHIVE::CM_DEFLATE] = 'deflate';
    $modes[ZIPARCHIVE::CM_DEFLATE64] = 'deflate64';
    $modes[ZIPARCHIVE::CM_PKWARE_IMPLODE] = 'pkware implode 1';
    //$modes[ZIPARCHIVE::CM_BZIP2] = 'bzip2';
    
    echo '<table>' . PHP_EOL;
    echo '  <tr>' . PHP_EOL;
    echo '    <td>Name</td>' . PHP_EOL;
    echo '    <td>Groesse ungepackt</td>' . PHP_EOL;
    echo '    <td>Groesse gepackt</td>' . PHP_EOL;
    echo '    <td>Methode</td>' . PHP_EOL;
    echo '  </tr>' . PHP_EOL;
    
    while(false !== $archiv->statIndex($i)) {
    
        $stat = $archiv->statIndex($i);
        $sizeCompressed += $stat['comp_size'];
        $sizeUncompressed += $stat['size'];
        $i++;
        echo '  <tr>' . PHP_EOL;
        echo '    <td>' . $stat['name'] . '</td>' . PHP_EOL;
        echo '    <td>' . $stat['size'] . ' bytes</td>' . PHP_EOL;
        echo '    <td>' . $stat['comp_size'] . 'bytes</td>' . PHP_EOL;
        echo '    <td>';
        if (isset($modes[$stat['comp_method']])) {
            echo $modes[$stat['comp_method']];
        } else {
            echo 'unbekannt';
        }
            echo '</td>' . PHP_EOL;
        echo '  </tr>' . PHP_EOL;
    }
    echo '<table>' . PHP_EOL;
    echo '<br /><pre>' . PHP_EOL;
    echo 'Anzahl Dateien: ' . $i . '<br />';
    echo 'Archivgroesse:    ' .  filesize($archivFile). '<br />';
    echo 'Groesse der Dateien im Archiv: <br />';
    echo 'ungepackt:      ' . $sizeUncompressed . ' bytes <br />';
    echo 'gepackt:        ' . $sizeCompressed . ' bytes <br />';
    echo '</pre>';
    
    ]]>
    Thu, 20 Aug 2009 12:53:42 +0000
    <![CDATA[Dateien in einem Zip-Archiv mit php und ZipArchiv auflisten]]> http://www.robo47.net/codeschnipsel/16-Dateien-in-einem-Zip-Archiv-mit-php-und-ZipArchiv-auflisten http://www.robo47.net/codeschnipsel/16-Dateien-in-einem-Zip-Archiv-mit-php-und-ZipArchiv-auflisten
    <?php
    $archiv = new ZipArchive();
    $archiv->open('archiv.zip');
    $i = 0;
    while(false !== $archiv->statIndex($i)) {
        $stat = $archiv->statIndex($i);
        echo $stat['name'] . '<br />';
        $i++;
    }
    echo 'Anzahl Dateien: ' . $i;
    ]]>
    Thu, 20 Aug 2009 12:50:12 +0000
    <![CDATA[Trackbacks sind zurück]]> http://www.robo47.net/blog/170-Trackbacks-sind-zurueck http://www.robo47.net/blog/170-Trackbacks-sind-zurueck Die Urls haben sich übrigends geändert, weil ich damit erstmal die alten Trackback-SPAM-Bots drausen halte die aktuell teilweise immernoch mehr als 40 Spamversuche pro Tag versuchen, an Urls die Seit ~ 2 Wochen nen 404 liefern ...]]> Thu, 20 Aug 2009 00:49:51 +0000 <![CDATA[Mein Umstieg auf Linux, der Weg in eine neue bessere Welt]]> http://www.robo47.net/blog/169-Mein-Umstieg-auf-Linux-der-Weg-in-eine-neue-bessere-Welt http://www.robo47.net/blog/169-Mein-Umstieg-auf-Linux-der-Weg-in-eine-neue-bessere-Welt Seit ungefähr 10 Monaten bin ich jetzt komplett auf Linux umgestiegen. Angefangen habe ich mit Kubuntu 8.04 (KDE als Windows-Manager), allerdings hat mir die KDE-Umgebung nicht wirklich zugesagt und als ich mir testweise Kubuntu 8.10 angeschaut, hatte ich das Gefühl Vista installiert zu haben, das hat mir dann überhaupt nicht zugesagt und so bin ich über Ubuntu 8.10 (Gnome als Windows-Manager) zu aktuell Ubuntu 9.04 gekommen.

    Allgemein lässt sich sagen ich will Linux unter keinen Umständen mehr missen, eine mächtige Kommandozeile (anfangs bash mittlerweile zsh), ein Paketmanager (apt) um kinderleicht sich mit dem kompletten Grundstock an Programmen zu versorgen und diese sowie das Betriebsystem aktuell zu halten zu bleiben,

    Der Umstieg war sogar viel einfacher als anfangs gedacht, fast meine kompletten Standardprogramme wie Thunderbird, OpenOffice , Firefox, Pidgin, Skype, VLC, Filezilla und RSSOwl sowie meine Entwicklungsumgebung bestehend aus Eclipse PDT, Subversion, Apache, MySQL, php, liefen alle auch nativ unter Linux.

    Als Ersatz für Winamp hab ich etliche Programme ausprobiert, darunter xmms, amarok, banshee, Songbird, Rhythmbox um schlussendlich dann bei Exaile zu landen.

    Das einzigen Programme für das ich auf Anhieb bisher keine wirkliche Alternative gefunden habe ist Pixmantec Rawshooter, ein Programm um digitale Photos im RAW-Format zu entwickeln. Die bisher getesten Alternativen haben irgendwie nicht wirklich in meinen Workflow gepasst, hatten gewisse für mich Grundlegende features nicht und waren meist auch ein gutes Stück langsamer. Daher nutze ich Rawshooter aktuell noch via Wine.

    Bei einigen Programmen und Tools hab ich auch für meinen Geschmack besseren Alternativen gefunden, als IDE nutze ich mittlerweile nicht mehr Eclipse PDT sondern Netbeans (aktuell 6.8 M1) das ich neben PHP im Studium z.b. auch für Java und C++ eingesetzt habe und das auch eine gute Unterstützung für ANT, PHPUnit, JUnit und ein paar weitere Tools (Benjamin Eberlei entwickelt gerade ein Plugin zur Nutzung von PHP_CodeSniffer in Netbeans) hat.

    Als Versionskontrollsystem bin ich recht schnell von Subversion zu Git gewechselt, anfangs indem ich Gits SVN-Support genutzt habe und aktuell sind alle Repros die ich nutze zu Git migriert und ich hab sie auf meinen Server via SSH erreichbar abgelegt.

    Enso Launcher den ich früher benutzt habe wurde durch Gnome-Do ersetzt, zwar nicht ganz so mächtig, aber für mich völlig ausreichend.

    Nachdem mir mein Thunderbird einfach mit der Zeit zu langsam wurde, nutze ich ihn jetzt nur noch als Mailarchiv und spiele aktuell mit Evolution herum. Etwas flotter, und standardmäßig schon PGP und einen Kalender integriert.

    Was mir unter Linux einfach gefällt ist die große Flexibilität und die vielen Alternativen die man bei vielen Tools und Programmen hat, eine Shell mit der man auch arbeiten kann (was würde ich ohne cat, tail, grep, xargs und co machen ?)

    Einige neue Programme sind auch dazu gekommen:
    Mit Zim habe ich eine nützliche Desktop-Wiki in der ich Todos, Notizen, Anleitungen, nützliche Befehle und anderen Kram hinterlegen kann, gekoppelt mit git sogar mit einer History.
    Vorher hatte ich noch Tomboy ausprobiert, aber Zim war am meisten nach meinem Geschmack.
    Tilda ist ein Terminal-Emulator der wie in vielen First-Person-Shootern per Knopfdruck von oben runterscrollt und auch noch mehrere Terminals in einzelnen Tabs zur Verfügung stellt, ausprobiert hatte ich hier vorher noch Yakuake, Kuake und YeahConsole.
    Gwibber ist nachdem mir Twitux nicht gefallen hat mein Twitter-Client geworden, die Alternativen hätten Adobe Air gebraucht, was mir da nicht wirklich zugesagt hat.

    ]]>
    Tue, 11 Aug 2009 21:56:55 +0000
    <![CDATA[Trackback und Kommentarspam]]> http://www.robo47.net/blog/168-Trackback-und-Kommentarspam http://www.robo47.net/blog/168-Trackback-und-Kommentarspam Akismet wird der Kram direkt als SPAM markiert, trotzdem werde ich wohl in Zukunft noch das ein oder andere einbauen um Akismet so wenig wie möglich bemühen zu müssen.
    Die Trackbackspam-Versuche auf die alten Trackback-Urls sind trotz 404 noch nicht so schnell gestorben und ich denke die werden auch noch einige Zeit weitergehen. Sobald ich die Funktion wieder eingebaut habe, werde ich dort wohl auch Akismet zur SPAM-Bekämpfung einsetzen oder ich schaue mir mal eine Alternative wie Typepad Antispam, Defensio oder Mollom an, alle 3 bieten auch eine API an über die man Kommentare und Trackbacks überprüfen kann.]]>
    Sat, 08 Aug 2009 09:56:52 +0000
    <![CDATA[Es geht weiter]]> http://www.robo47.net/blog/167-Es-geht-weiter http://www.robo47.net/blog/167-Es-geht-weiter public.robo47.net hier unter einer Plattform zusammenzufassen. Das neue System hinter dem Blog hat seine Ursprünge 2008 und hat eine lange Evolution hinter sich. Von Zend Framework 1.5 bis hin zur aktuellen Version 1.9. Wer mehr von der Technik hinter der Seite erfahren will findet unter Über die Technik eine Übersicht.
    Ich hab auch vor in Zukunft hier wieder etwas aktiver hier zu sein soweit es die Uni zulässt. Jetzt werde ich mal noch die Kontaktanfragen die liegen geblieben sind abarbeiten und dann kann ich mich hier noch etwas um die neuen Inhalte, Blogeinträge und soweiter kümmern.
    Was bisher fehlt im Vergleich zur letzen Version ist die Trackback-Funktion, das wird wohl in einem der nächsten Updates kommen Dafür gibt es aber z.b. einen Kommentar-Feed und für jeden Blogeintrag einen Kommentar-Feed.
    Einige Spielereien sind aktuell noch deaktiviert, weil ich den Footer nicht noch größer machen wollte, dazu gehört z.b. eine Latest-Tweets-Integration und eine Anzeige von Alben von Jamendo.
    ]]>
    Fri, 07 Aug 2009 21:10:40 +0000
    <![CDATA[Datenbank-Backups mit git (mysql, postgresql, etc)]]> http://www.robo47.net/text/12-Datenbank-Backups-mit-git-mysql-postgresql-etc http://www.robo47.net/text/12-Datenbank-Backups-mit-git-mysql-postgresql-etc Datenbank-Backups mit git (mysql, postgresql, etc)

    Ein Problem auf das man früher oder später stößt wenn man Backups von seinen Datenbanken macht, ist dass die Backups immer größer werden, und abhängig davon wie viele man speichern will (und wie oft man welche macht)

    Sven hatte mich vor langer Zeit schon einmal auf die Möglichkeit aufmerksam gemacht, doch ein Versionsverwaltungs-Tool wie Subversion für die Speicherung der Backups zu nutzen. Da Versionsverwaltungstools idealerweise ASCII-Dateien nicht jedes mal neu speichern, sondern über Diffs nur die Änderungen speichert, spart man dabei eine ganze Menge Platz. Bei GIT kommt hier jetzt noch eine sehr gute Kompression hinzu. Um nicht nur Online ein Backup zu haben (kann ja auch mal sein dass die ganze Kiste abraucht), kann man sich dann das Git-Repo über ein Protokol seiner Wahl (ssh, git, https) einfach clonen und mit git pull up2date halten. Auch hier entsteht nur sehr wenig Traffic, da nur die Änderungen übertragen werden.

    Einfaches Beispiel für ein mysql backup + Datei adden + Commiten der Daten + Aufräumen und komprimieren

    DATE=`date '+%Y-%m-%d__%H-%M-%S'` mysqldump -uUsername -pPassword databasename > /path/to/gitrepo/dump.sql cd /path/to/gitrepo/ git add dump.sql git commit -am "Backup from ${DATE}" git gc ]]>
    Thu, 16 Jul 2009 12:00:00 +0000
    <![CDATA[PHP PECL Extensions Fehler]]> http://www.robo47.net/text/11-PHP-PECL-Extensions-Fehler http://www.robo47.net/text/11-PHP-PECL-Extensions-Fehler PHP Configure und Compile Fehler

    Mögliche auftretenden Fehler die beim Installieren von PECL-Extensions beim Fehlen von Bibliothkenen oder deren dev(el)-Paketen auftreten können und welche Pakete man unter Debian/Ubuntu dann installieren muss.

    Übersicht

    configure: error: The required libssh2 library was not found. You can obtain that package from http://sourceforge.net/projects/libssh2/

    Fehlende Bibliothek: libssh2

    apt-get install libssh2-1 libssh2-1-dev
    ]]>
    Thu, 16 Jul 2009 12:00:00 +0000
    <![CDATA[SSH dynamisch nur für gewisse IPs erlauben dynamische IP-Whitelist]]> http://www.robo47.net/text/10-SSH-dynamisch-nur-fuer-gewisse-IPs-erlauben-dynamische-IP-Whitelist http://www.robo47.net/text/10-SSH-dynamisch-nur-fuer-gewisse-IPs-erlauben-dynamische-IP-Whitelist Auf diesem Weg kann man in Kombination mit einem Cronjob und einem weiteren Script (sei es PHP oder eine Sprache) dynamisch verschiedenen IPs erlauben SSH zu nutzen.
    Der Cronjob ist auch nötig, weil PHP das unter dem Benutzer des Webservers (Modul) oder einem eigenen Benutzer (suexec) läuft, üblicherweise (und sinnvoller) Weise keinen Schreib-Zugriff auf das /etc/-Verzeichnis hat.

    Anmerkung: Das ist hier alles quick and dirty ausgeführt, nur um zu zeigen was für Möglichkeiten man hat.

    Mein Problem war es einer Usergruppe (Angemeldeten Benutzern eines Forums) Zugriff auf einen SFTP/SSH-Account zu geben und diesen Usern einen möglichst einfachen Zugriff bei gleichzeitigem Schutz vor den üblichen Bruteforce-Attacken. Dazu habe ich dann folgendes kleines Script benutzt (es ist für das Simple Maschines Forum (SMF) geschrieben, sollte allerdings sehr einfach an ein anderes Forum angepasst werden können.)

    <?php
    error_reporting(E_ALL);
    $res = mysql_connect('host','user','pw') or die(mysql_error());
    mysql_select_db('databasename',$res) or die(mysql_error());
    $query = mysql_query("SELECT memberIP,memberIP2 from smf_members") 
                  or die(mysql_error());
    if (mysql_num_rows($query) > 0)
    {
        while($data = mysql_fetch_assoc($query))
        {
            $global[] = $data['memberIP'];
            $global[] = $data['memberIP2'];
        }
    }
    file_put_contents('./hosts.allow','sshd: '.implode("\n".'sshd: ', $new));
    

    Der Cronjob muss jetzt folgendes machen, er muss das PHP-Script aufrufen (entweder über die shell oder über wenn es via URL erreichbarbar ist über wget/lynx/curl) und dann die vom PHP-Script erzeugte Datei an den richtigen Platz kopieren:

    wget --output-document=/dev/null  http://www.domain.tld/allowscript/allowscript.php
    cp /pfad/zum/allowscript/hosts.allow  /etc/hosts.allow
    

    Dadurch dass der Cronjob nur alle Minute ausgeführt wird, kommt es zwar zu einer kurzen Wartezeit von bis zu einer Minute bis man nach dem einloggen im Forum Zugang zum SFTP/SSH bekommt, aber das sollte akzeptabel sein.

    Ein Nachteil der besteht, ist dass man bei einem normalen DSL-Anbieter, üblicherweise alle 24 Stunden einen Disconnect hat raus und muss danach erst wieder aufs Forum, auch wenn man gerade vor fünf Minute auf dem Forum war. Wer ne feste IP hat, hat damit kein Problem, es lässt sich allerdings auch noch erweitern indem man nur die IPs der Leute einträgt die innerhalb der letzen XX Stunden z.b. auf dem Forum waren, einen Post gemacht haben .... ist also auch weiter ausbaufähig.

    ]]>
    Thu, 16 Jul 2009 12:00:00 +0000
    <![CDATA[Hashwert einer Zeichenkette berechnen]]> http://www.robo47.net/tool/10-Hashwert-einer-Zeichenkette-berechnen http://www.robo47.net/tool/10-Hashwert-einer-Zeichenkette-berechnen Thu, 16 Jul 2009 12:00:00 +0000 <![CDATA[sh: line 1: host: command not found und Invalid query name 1 im Apache-Log]]> http://www.robo47.net/text/9-sh-line-1-host-command-not-found-und-Invalid-query-name-1-im-Apache-Log http://www.robo47.net/text/9-sh-line-1-host-command-not-found-und-Invalid-query-name-1-im-Apache-Log Ich habe vor einiger Zeit folgenden immer wiederkehrenden Fehler in den Log-Files meines Apachen gefunden:

    sh: line 1: host: command not found

    Das ganze deutet zuerst darauf hin, dass das Tool host (Ein Werkzeug zum Kontaktieren von DNS-Servern) nicht installiert ist.

    Unter Ubuntu/Debian brachte das installieren von host eine scheinbare Abhilfe, allerdings nur scheinbar, stattdessen häufte sich jetzt ein neuer Fehler im Log: Invalid query name 1

    Nach langem hin und her konnte ich als indirekten schuldigen PHP und dort das SMF-Forum dafür verantwortlich machen. Schuld war dabei die Einstellung hostname lookups des SMFs [und das dadurch aufgerufene gethostbyaddr()] wie ich dort im Forum dann herausfand. Als Lösung des Problems konnte man nun entweder dieses Feature ausschalten, oder wieder host deinstallieren und stattdessen dann bind9-hosts intalliere, da host gewisse Parameter nicht unterstützt die bind9-hosts kann.

    Lösung

    Unter Debian/Ubuntu:

    apt-get install bind9-host
    

    Thread im SMF-Forum

    ]]>
    Thu, 16 Jul 2009 12:00:00 +0000
    <![CDATA[Hashwert einer Datei bestimmen]]> http://www.robo47.net/tool/9-Hashwert-einer-Datei-bestimmen http://www.robo47.net/tool/9-Hashwert-einer-Datei-bestimmen Thu, 16 Jul 2009 12:00:00 +0000 <![CDATA[Datenbank-Fehlerbehandlung in PHP - verschiedene Wege und Möglichkeiten]]> http://www.robo47.net/text/13-Datenbank-Fehlerbehandlung-in-PHP-verschiedene-Wege-und-Moeglichkeiten http://www.robo47.net/text/13-Datenbank-Fehlerbehandlung-in-PHP-verschiedene-Wege-und-Moeglichkeiten Dieser kleine Artikel soll sich mit Thema der Fehlerbehandlung in PHP, speziell beim Arbeiten mit Datenbanken auseinandersetzen, dabei werden verschiedene Wege und Möglichkeiten aufgezeigt und verglichen. Er zeigt dabei auch etwas meine eigene Weiterentwicklung über die Jahre die ich mich schon mit PHP und anderen Programmiersprachen beschäftige.

    1) Fehlerbehandlung via die()

    Der wohl am meisten verbreitete und bekannteste Weg zur "Fehlerbehandlung" in PHP bei der Benutzung von Datenbanken dürfte wohl die benutzung von die() sein. Man findet sie in Foren, Code-Beispielen in vielen Blogs und Büchern und Tutorials.

    Einfaches Beispiele:

    <?php
    
    $connection = mysql_connect('localhost', 'baa', 'foo')  or die(mysql_error());
    $selected   = mysql_select_db('baa', $connection)       or die(mysql_error());
    $query      = mysql_query('SELECT baa FROM foo')        or die(mysql_error());
    
    ?>
    

    Einen wirklichen Vorteil dieser Methode ausser der Simplizität mit der sie sich im Code einbinden lässt gibt es eigentlich nicht. Aber dafür einige Nachteile: Das Script bricht an dieser Stelle komplett ab und der Kunde bzw. User der Seite bekommt einerseits eine unschöne Fehlermeldung undabhängig vom Fehler und der Meldung auch Informationen darüber was in dem Script gerade passiert / abläuft (Sicherheit) Desweiteren ist es nicht wirklich gleich ersichtlich WO genau der Fehler aufgetreten ist, da keinerlei Angaben über die betroffene Datei und Zeilennummer gemacht wird und keine Informationen zum Query und verwendeten Parametern bekannt sind. Ein weiteres Problem bei der Verwendung von die() ist, dass wenn der Fehler nicht gerade im Entwicklungsbetrieb einem Entwickler auffällt er sehr wahrscheinlich nie gemeldet wird.

    Wenn auch am weitesten verbreitet sollte man von dieser Methode möglichst schnell Abstand nehmen, weil sie keine wirkliche Fehlerbehandlung ermöglicht, sondern nur eine möglicherweise sicherheitsbedenkliche Fehlermeldung ausgibt und dann den kompletten Script-Ablauf stoppt. Gerade mit der dann meistens nicht vorhandenen Trennung von Code und Layout ist eine zerhackte und unschöne Ausgabe schon vorprogrammiert.

    2) Erweiterte Fehlerbehandlung mit Logging/Mailing via einfacher Wrapper-Funktion

    Einen einfachen Weg stellt die Verwendung von Wrapper-Funktionen für die gängigen Befehle: Datenbank-Verbindung aufbauen, Datenbank auswählen und die Datenbankabfragen selbst dar. Hier kann man sehr einfach und projektübergreifend zumindest ein Logging (z.b. mit PEAR Log oder Zend_Log) oder Email versenden (z.b. mit Swiftmailer, phpmailer, PEAR Mail oder Zend_Mail) für Fehler implementieren und neben dem Query auch Informationen z.b. Über die Url ($_SERVER), in welcher Datei + Zeile der Fehler aufgetreten ist (debug_backtrace()) und weitere Informationen die den Request betreffen ($_POST, $_GET, $_COOKIE, $_SESSION ... etc) mitgespeichert werden können.

    Einfaches Beispiele:

    <?php
    
    function logError($message)
    {
        $error = PHP_EOL . $message . PHP_EOL .
                 PHP_EOL . print_r(debug_backtrace(), true);
        file_put_contents('error.log', $error);
        mail('admin@project.tld', 'Fehler in Project XYZ', $error);
    }
    
    function connectToDatabase($host, $username, $password, $database)
    {
        $connection = mysql_connect($host, $username, $password);
        if ($connection === false) {
            echo 'Es ist ein unerwarteter Fehler aufgetreten, der Administrator wurde informiert';
            logError("Fehler beim Aufbau der Verbindung zur Datenbank: " . mysql_error());
            exit();
        }
        $selected = mysql_select_db($database);
        if ($selected === false) {
            echo 'Es ist ein unerwarteter Fehler aufgetreten, der Administrator wurde informiert';
            logError("Fehler bei der Auswahl der Datenbank" . mysql_error());
            exit();
        }
    }
    
    function queryDatabase($query)
    {
        $query = mysql_query($query);
        if ($query === false) {
            echo 'Es ist ein unerwarteter Fehler aufgetreten, der Administrator wurde informiert';
            logError("Fehler im Query: " . $query . PHP_EOL . PHP_EOL . "Fehler: " . mysql_error());
            exit();
        }
    }
    
    ?>
    

    Dieses Methode erlaubt schon eine deutlich bessere Behandlung von Fehlern, der User bekommt keine kryptische sondern eine möglichst neutrale Fehlermeldung, der Verantwortliche des System wird informatiert und kann sich um das Problem kümmern. Nachteil ist auch hier weiterhin, dass es zu einem kompletten Abbruch des Scriptes kommt und der Fehler in keiner Form behandelt wird.

    3) Richtige Fehlerbehandlung mit Exceptions

    Eine weitere Methode der Behandlung von Fehler (nicht nur beim arbeiten mit Datenbanken) ist das Arbeiten mit Exceptions. Hier wird in einem try-Block der eigentliche Code geschrieben und in Catch-Blöcken kann man die auftretenden Exceptions auffangen und dann passend behandeln. Auch die Fehlerbehandlung über mehrere Ebenen ist möglich indem man verschachtelten try/catch-Blöcke nutzt und z.b. in einem catch-Block nach der hier möglichen Behandlung die Exceptions "weiterwirft" um sie weiter oben weiter zu behandeln

    Im Bezug auf Datenbanken kan man zu diesem Zwecke entweder das Rad neu erfinden, wenn man darauf steht und sich einen eigenen Wrapper um die mysql_*, mysqli_* Funktionen oder pg_sql (oder auch die Funktionen einer anderen Datenbank) basteln oder man setzt auf eine der schon existierenden Datenbank-(Abstraktions)Klasse die von sich aus Exceptions bei Fehlern werfen. Möglichkeiten gibt es hier viele, die seit PHP 5.1 standardmäßig vorhandene Extensions PDO (vor 5.1 als PECL-Extensions) oder auch komplexere (teilweise auf PDO aufbauende) Systeme und Komponenten von Frameworks die zum Teil noch um einiges mehr bieten als nur einen einfachen Wrapper um eine Datenbank-API (z.b. ORM-Systeme):

    Weitere Möglichkeiten findet man via Google oder auch bei PHP Classes

    Einfaches Beispiele:

    <?php
    
    function PrintErrorMessage($message)
    {
        /* Ausgabe einer Fehlerseite */
    }
    
    function LogException($e)
    {
        /* Logging der Exception zusammen mit Umgebungsvariablen und co */
        /* Senden einer Email */
    }
    
    /* ... */
    
    $dir = BASE_DIR . '/foo';
    
    try {
    
        mkdir($dir);
        touch($dir . 'File1');
        touch($dir . 'File2');
        $db->query("INSERT INTO table (id, field1, field2) VALUES (4, 'Baa', 'Foo')");
    
    } catch (DBException $e) {
        // Gemachte Dinge rückgängig machen
        unlink($dir . 'File1');
        unlink($dir . 'File2');
        rmdir($dir);
    
        LogException($e);
        PrintErrorMessage('Beim anlegen eines Datensatzes ist ein Problem aufgetreten');    
    }
    
    ?>
    

    Zusammenfassung / persönlicher Fazit

    Der Weg der mir mittlerweile am besten zusagt ist die Benutzung von Exceptions, da ich hier in Kombination mit strikter Trennung von Code und Layout und Ausgabenpufferung nicht nur die Möglichkeit habe Fehler zu loggen und dem User eine ordentliche Fehlerseite auszugeben, sondern auch die Möglichkeit habe im catch-Block direkt auf Fehler zu reagieren, Dinge rückgängig zu machen wie erstellte Datensätze, Ordner, Dateien (Stichwort Transaktionen).

    Methode Vorteile Nachteile
    Methode 1
    • Einfach und schnell in der Anwendung
    • Keinerlei Fehlerbehandlung
    • Keinerlei Logging
    • Abbruch des Scripts
    Methode 2
    • Einfacher in der Anwendung als Methode 1
    • Möglichkeit des Loggings
    • Abbruch des Scripts
    Methode 3
    • Möglichkeit des Loggings
    • Möglichkeit der Fehlerbehandlung
    • Höhere Komplexität und teilweise mehr Aufwand
    ]]>
    Thu, 16 Jul 2009 12:00:00 +0000
    <![CDATA[Martin Wieser - Kamera Setup für Canon EOS 1D(s) Mark II (N)]]> http://www.robo47.net/text/4-Martin-Wieser-Kamera-Setup-fuer-Canon-EOS-1D-s-Mark-II-N http://www.robo47.net/text/4-Martin-Wieser-Kamera-Setup-fuer-Canon-EOS-1D-s-Mark-II-N Hier gibt es das Dokument Kamera Setup für die Kameras Canon EOS 1D(s) Mark II (N) von Martin Wieser.

    PDF-Dokument Martin Wieser - Kamera Setup.pdf


    Get Adobe Reader

    ]]>
    Thu, 16 Jul 2009 12:00:00 +0000
    <![CDATA[SSH-Fingerprint unter Linux auslesen]]> http://www.robo47.net/text/1-SSH-Fingerprint-unter-Linux-auslesen http://www.robo47.net/text/1-SSH-Fingerprint-unter-Linux-auslesen Da die meine am meisten verwendeten Clients für SSH-Verbindungen (Putty/Filezilla/etc) nirgends eine Klartext-Variante der Fingerprint speichern denen man vertraut hat speichern ich aber von Filezilla 2.X auf Filezilla 3.X wechseln wollte und meine Liste mit den Fingerprints nicht dabei hatte, habe ich nach einem anderen Weg gesucht und gefunden.
    Mittels dem folgenden Befehl lässt sich der Fingerprint eines

    ssh-keygen -lf keyfile
    

    Die Keyfiles findet man dann beispielsweise in /etc/ssh/ ansonsten muss man mal in der config des sshd nachschauen.

    ]]>
    Thu, 16 Jul 2009 12:00:00 +0000
    <![CDATA[SSL Zertifikat Informationen auslesen]]> http://www.robo47.net/tool/2-SSL-Zertifikat-Informationen-auslesen http://www.robo47.net/tool/2-SSL-Zertifikat-Informationen-auslesen Thu, 16 Jul 2009 12:00:00 +0000 <![CDATA[Canon Digital Photo Professional (DPP) 3.7.2 und weitere Canon Tools ohne CD installieren]]> http://www.robo47.net/text/2-Canon-Digital-Photo-Professional-DPP-3.7.2-und-weitere-Canon-Tools-ohne-CD-installieren http://www.robo47.net/text/2-Canon-Digital-Photo-Professional-DPP-3.7.2-und-weitere-Canon-Tools-ohne-CD-installieren Da sich aktuell die runtergeladenen Installer auf der Canon-Seite nur als "Updater" zu erkennen geben und sich damit ohne eine ältere Version der Programme, die Updates nicht installieren lassen, habe ich nach einem Weg gesucht wie man auch ohne eine installierte Version in den Genuss aktueller Canon-Programme kommt. Da es ja durchaus mal vorkommen kann, dass man die CD die bei der Kamera dabei war nicht mehr hat, nicht findet oder zum Beispiel eine Kamera gebraucht ohne Handbücher und CDs gekauft hat, ist dieser Wunsch durchaus nachvollziehbar.

    Diese kurze Anleitung beschreibt wie man Canon Digital Photo Professional (DPP) 3.7.2 und damit auch weitere Canon Programme wie den Canon Zoombrowser EX 6.4.1 und das Canon EOS Utility 2.6.1 ohne CDs mit einer alten Version installieren kann. Es funktioniert NICHT für das Panorama-Tool PhotoStitch. Mit großer Sicherheit wird das auch für zukünftige Versionen der Programme funktionieren, sollten die alten Versionen nicht aus dem Netz genommen werden.

    Der Trick ist eigentlich recht simpel und wurde von Scorpio im DLSR-Forum beschrieben: DPP Software oder Download.

    Um eine aktuelle Version zu installieren braucht man eine alte Version die installiert ist.
    Das Problem das sich jetzt aber stellt, woher bekommt man eine alte Version von DPP ? Also habe ich ein bißchen Google bemüht und bin am Ende auf den Public FTP von Canon Japan gestoßen, dort liegen in dem Unterverzeichnis /pub/driver/digitalcamera/ einige Version von Canon Digital Photo Professional (DPP) und weiteren Programmen in japanisch. Dass DPP Japanisch ist, ist uns erstmal egal, weil wir später eh eine neuere Version in einer anderen Sprache drüberinstallieren wollen. Ich habe für meine Installation Canon Digital Photo Professinal 2.11 (DPP) [dpp211w.exe] genommen.

    Im nächsten Schritt brauchen wir einen Entpacker wie 7Zip (Winrar oder Winzip sollten auch funktionieren). Mit diesem Entpacker, entpacken wir die runtergeladene Version von DPP in ein Verzeichnis unserer Wahl. Dort wechseln wir in den Ordner: \INSTALL\COMMON und führen die Install.exe aus.

    Dank einer sehr guten Oberfläche bei der man eigentlich nur nach den großen Buttons suchen muss die grün werden wenn man darüber fährt, lässt sich DPP nun auf Japanisch recht einfach installieren.

    Ist Canon Digital Photo Professinal (DPP) erstmal auf Japanisch installiert, kann man sich nun die aktuellen Versionen der Canon-Programme besorgen und drüberinstallieren. Die findet man auf der Canon-Seite, ein paar direkte Links gibts hier:

    Windows:

    Mac:

    Eine weitere von mir bisher noch nicht geteste Variante, die etwas weniger aufwändig ist, gibt es im dslr-forum.
    Dank für den Link geht an Christian Aysner.

    Weitere Links

    Englische Beschreibung für Installation ohne CD für 32bit und 64bit Windows via anlegen von Registry-Keys

    ]]>
    Thu, 16 Jul 2009 12:00:00 +0000
    <![CDATA[TAR unter Linux verwenden]]> http://www.robo47.net/text/3-TAR-unter-Linux-verwenden http://www.robo47.net/text/3-TAR-unter-Linux-verwenden Mit dem Programm tar kann man unter Linux Tar-Archive (auch Tarballs genannt) erstellen, entpacken und auf Wunsch auch mit gzip, bzip2 komprimieren um Platz zu sparen. Bzip2 erreicht hier in den meisten Fällen eine etwas höhere Kompression und schafft es damit noch kleinere Dateien zu erstellen, dafür dauert die Kompression aber auch länger.
    Übliche Endungen für Tarballs und komprimierten Tar-Archiven sind:

    • .tar (ohne Kompression)
    • .tar.gz (gzip)
    • .tgz (gzip)
    • .tar.Z (gzip)
    • .tar.bz2 (bzip2)
    • .tbz (bzip2)
    • .tbz2 (bzip2)
    • .tbz2 (bzip2)
    • .tbz2 (bzip2)
    • .tar.7z (7zip)

    Tar-Archive unter Linux erstellen

    ohne Kompression:

    tar -cvf dateiname.tar ./ordnername
    

    mit bzip2-Kompression:

    tar -cjvf dateiname.tar.bz2 ./ordnername
    

    mit gzip-Kompression:

    tar -czvf dateiname.tar.gz ./ordnername
    

    Tar-Archive unter Linux entpacken

    ohne Kompression:

    tar -xvf dateiname.tar
    

    mit bzip2-Kompression:

    tar -xjvf dateiname.tar.bz2
    

    mit gzip-Kompression:

    tar -xzvf dateiname.tar.gz
    

    Ein Vorteil von tar im Einsatz unter Linux ist, dass für jede Datei und jedes Verzeichnis die Dateirechte und der Besitzer mitgespeichert wird, so werden diese auch beim entpacken wiederhergestellt und damit eignet sich tar in vielen Fällen besser für das erstellen von Backups unter Linux als beispielsweise ZIP oder RAR-Archive.

    ]]>
    Thu, 16 Jul 2009 12:00:00 +0000
    <![CDATA[Vhost via 301 - Moved Permanently Header weiterleiten]]> http://www.robo47.net/text/7-Vhost-via-301-Moved-Permanently-Header-weiterleiten http://www.robo47.net/text/7-Vhost-via-301-Moved-Permanently-Header-weiterleiten Mit diesem Code direkt in der Konfiguration des Apache-Webservers, kann man einen Vhost via 301 - Moved Permanently an eine andere Adresse (Domain, Subdomain) Weiterleitung unter Beibehaltung der Pfade:

    <VirtualHost *:80>
        ServerName subdomain.domain.tld
        RedirectMatch 301 (.*) http://www.domain2.tld$1
    </VirtualHost>
    

    Dabei wird aus subdomain.domain.tld/asdf/ dann www.domain2.tld/asdf/

    Sämtliche Pfad, Datei und Parameter-Angaben bleiben erhalten

    Will man nur auf die Domain selbst weiterleiten ohne die Pfad und Dateiangaben, dann macht man das über:

    <VirtualHost *:80>
        ServerName subdomain.domain.tld
        RedirectMatch 301 (.*) http://www.domain2.tld
    </VirtualHost>
    
    ]]>
    Thu, 16 Jul 2009 12:00:00 +0000
    <![CDATA[PHP Configure und Compile Fehler]]> http://www.robo47.net/text/6-PHP-Configure-und-Compile-Fehler http://www.robo47.net/text/6-PHP-Configure-und-Compile-Fehler PHP Configure und Compile Fehler

    Mögliche auftretenden Fehler beim selbstkompilieren von php und welche Pakete man unter Debian/Ubuntu dann installieren muss.

    Übersicht

    Ein Problem das sehr oft auftritt wenn man frisch php kompiliert oder eine neue Extension hinzufügen will ist das fehlen der -dev bzw -devel Paketen für die passende Extension. Die Angegeben Versionsnummer können natürlich abhängig von der Distribution und Version abweichen, aber es sollte zumindest einen hinweis in die richtige Richtung geben was dem System noch fehlt, die Beispiele beziehen sich alle auf Debian ETCH.

    configure: error: Please reinstall the BZip2 distribution

    Fehlende Bibliothek: libbz2

    apt-get install libbz2-dev

    configure: error: Please reinstall the libcurl distribution - easy.h should be in <curl-dir>/include/curl/

    Fehlende Bibliothek: libcurl

    apt-get install libcurl3-dev

    Debian Lenny:

    libcurl4-openssl-dev

    configure: error: Cannot find MySQL header files under yes.

    Fehlende Bibliothek: libmysql

    apt-get install libmysqlclient15-dev

    configure: error: DBA: Could not find necessary header file(s)

    Fehlende Bibliothek: libgdbm

    apt-get install libgdbm-dev

    configure: error: libjpeg.(a|so) not found

    Fehlende Bibliothek: libjpeg

    apt-get install libjpeg62 libjpeg62-dev

    configure: error: libpng.(a|so) not found

    Fehlende Bibliothek: libpng

    apt-get install libpng12-0 libpng12-dev

    configure: error: xml2-config not found. Please check your libxml2 installation.

    Fehlende Bibliothek: libxml2

    apt-get install libxml2 libxml2-dev

    configure: error: mcrypt.h not found. Please reinstall libmcrypt.

    Fehlende Bibliothek: libmcrypt

    apt-get install libmcrypt4 libmcrypt-dev

    configure: error: Please reinstall libmhash - I cannot find mhash.h

    Fehlende Bibliothek: libmhash

    apt-get install libmhash2 libmhash-dev

    configure: error: Please reinstall readline - I cannot find readline.h

    Fehlende Bibliothek: libreadline

    apt-get install libreadline5-dev

    configure: error: cannot find mm library

    Fehlende Bibliothek: libmm

    apt-get install libmm-dev libmm14

    configure: error: Cannot find libtidy

    Fehlende Bibliothek: libtidy

    apt-get install libtidy-dev libtidy-0.99-0

    configure: error: xslt-config not found. Please reinstall the libxslt >= 1.1.0 distribution

    Fehlende Bibliothek: libxslt

    apt-get install libxslt1-dev libxslt1.1
    ]]>
    Thu, 16 Jul 2009 12:00:00 +0000
    <![CDATA[Domain vor Domainumzug auf neuen Server testen]]> http://www.robo47.net/text/8-Domain-vor-Domainumzug-auf-neuen-Server-testen http://www.robo47.net/text/8-Domain-vor-Domainumzug-auf-neuen-Server-testen Ein vielen bekanntes Problem wenn man eine Domain (sei es ein Forum, ein Blog, ein Shop oder was sogar mehrere Systeme) umziehen will, ist es vorher zu überprüfen, ob das System denn einwandfrei läuft, bevor auch die DNS-Einträge für die Domain angepasst werden. Hier mal ein paar Tips wie man so einen Umzug vorher besser testen kann ohne dass das böse erwachen erst nach dem Umschalten der Domain kommt.

    Als erstes sollte man sich um die Datenbank kümmern. Irgendwann zu der Zeit die laut Log-Dateien die am wenigsten frequentierte ist, schaltet man das System in den hoffentlich vorhandenen Wartungsmodus.
    Dann erstellt man einen aktuellen Dump der Datenbank, diesen spielt man dann auf dem neuen Server ein und ändert anschließend die Datenbankdaten im alten System auf die IP, Benutzername, Passwort und Datenbankname des neuen Servers.
    Das ganze erfordert allerdings auch, dass beim neuen Anbieter die (MySQL-)Datenbank von außen erreichbar ist, was bei vielen Webspace-Hostern nicht der Fall ist!
    Dann kann man den Wartungsmodus erstmal beenden. (Wichtig ist hierbei, dass das System keine Daten mehr ins Dateisystem speichert, die relevant sind, bei Foren sollte man z.b. während der Umzugsphase Datei-Anhänge, neue Avatare und ähnliche Features deaktivieren.) Dann kann man fortfahren und die kompletten Inhalte vom FTP des alten Servers auf den neuen Server umziehen.

    Hat man jetzt einen eigenen Server, sollte man den passenden vhost für die Domain schon anlegen, andernfalls sollte man seinen Provider darum bitten dies zu tun (Wenn man nur Webspace bei dem Provider hat). Jetzt kann man indem man in der hosts-Datei des Betriebssystem (Windows: C:\WINDOWS\system32\drivers\etc\hosts | Linux: /etc/hosts | MacOS: /etc/hosts) einen Eintrag für die Domain macht:

    IP.DES.NEUEN.SERVERS       www.domain.tld
    IP.DES.NEUEN.SERVERS domain.tld

    schon das System auf dem neuen Server einrichten und testen. Dieser Eintrag macht nichts anderes, als die IP für diese Domain nicht mehr vom DNS-Server des Providers zu beziehen, sondern fest einzustellen, damit kann man selbst ohne dass die anderen User der Seite es mitbekommen, schon auf dem neuen System surfen, testen und arbeiten.

    Nachdem man das komplette System jetzt auf dem neuen Server eingerichtet hat, kann man dann den Domainumzug starten. (Wichtig ist, jetzt die Einträge in der Hosts-Datei wieder zu löschen, damit man auch mitbekommt, wann die Domain beim eigenen Provider als umgezogen gilt.)

    Ein paar Dinge die man aber auf jeden Fall noch beachten sollte sind, wenn man die Domain umzieht, werden irgendwann die DNS-Einträge geändert (sobald diese geändert sind, ist die Domain auf dem neuen Server erreichbar, theoretisch), bis diese Änderung allerdings auf allen DNS-Servern bei allen Providern angekommen ist, können bis zu 24 Stunden vergehen. Heißt, es kann sein, dass man selbst zwar schon wenn man die Domain eingibt auch auf den neuen Server kommt, andere Leute aber nicht (selbst wenn sie beim gleichen Internet Provider sind). Daher sollte man jetzt keinesfalls schon alle Features wie Dateiuploads aktivieren, sonst kann es passieren, dass die Leute die noch auf dem alten Server sind, dort eine Datei hochladen, die dann nicht auf dem neuen Server liegt. Um so ein Problem zu umgehen kann man einfach, nach z.B. 24 Stunden, wenn man meint, dass genug Zeit vergangen ist, auf dem alten Server das Forum/den Shop (oder was auch immer) durch eine Informationsseite ersetzen, dass das Projekt gerade umzieht und bald wieder erreichbar sein sollte, dann kann man auch wieder die ganzen Features auf dem neuen Server aktivieren und muss keine Angst haben, dass es zu verschwundenen Dateianhängen oder ähnlichem kommt.

    Da ich im Oktober 2006 selbst ein paar Umzüge machen musste haben sich diese Tricks als sehr praktisch erwiesen, weil man zuverlässig vorher schon seine Systeme testen kann, ohne dass es zu längeren Ausfällen kommt. Lustiger weise waren die Suchmaschinenbots mit die letzen die meine neue IP bekommen haben, denn z.B. Google und Yahoo waren teilweise bis zu 6 Stunden, nachdem ich hier die Domain schon wieder auf dem neuen Server erreichen konnte, auf dem alten unterwegs und haben ein paar Foren und Seiten gespidert.

    ]]>
    Thu, 16 Jul 2009 12:00:00 +0000
    <![CDATA[Auslösungen auslesen FAQ]]> http://www.robo47.net/text/5-Ausloesungen-auslesen-FAQ http://www.robo47.net/text/5-Ausloesungen-auslesen-FAQ Ich freue mich jederzeit über Fragen, Ergänzungen, Vorschläge und Tipps dieser Seite und die FAQ betreffend! Dafür könnt ihr das das Kontaktformular dieser Seite benutzen.
    Das Tool zum Auslösungen auslesen finden sie hier: Auslösungen auslesen

    1. Welche Kameras werden unterstützt ?
    2. Wie viele Auslösungen schafft meine Kamera ?
    3. Was kann ich machen wenn meine Kamera nicht unterstützt wird ?
    4. Wie funktioniert das ganze ?
    5. Sind die Zahlen zuverlässig ?
    6. Warum wird meine Kamera nicht unterstützt ?
    7. Meine Kamera steht nicht in der Liste aber es werden trotzdem Auslösungen ausgelesen
    8. Meine Kamera steht in der Liste aber es können keine Auslösungen ausgelesen werden
    9. Ich habe eine neue Kamera gekauft und der Zähler fing nicht bei 0 an
    10. Welche Datei-Formate werden unterstützt ?
    11. Meine Bilder sind größer als das Uploadlimit, was kann ich tun ?
    12. Kann ich die Auslösungen auch offline auslesen ?
    13. Unter welchem Betriebsystem funktioniert diese Seite ?
    14. Unter welchem Browser funktioniert diese Seite ?
    15. Welche Version von Exiftool wird verwendet ?
    16. Meine Frage wird nicht von der FAQ beantwortet



     

    Welche Kameras werden unterstützt ?

    Unterstützt werden nur Kameras bei denen die Anzahl der Auslösungen in den Exifs des Bildes gespeichert werden und bei denen bekannt ist wie und wo in den Exifs diese Informationen gespeichert werden, damit Exiftool sie auslesen kann.

    Unterstützt Kameras Nicht unterstützte Kameras
    • Canon EOS 1D
    • Canon EOS 1D Mark II
    • Canon EOS 1D Mark II N
    • Canon EOS 1Ds Mark II
    • Canon EOS 1Ds (teilweise)
    • Nikon D2xs
    • Nikon D2hs
    • Nikon D200
    • Nikon D3
    • Nikon D3x
    • Nikon D300
    • Nikon D40
    • Nikon D40x
    • Nikon D50
    • Nikon D60
    • Nikon D70
    • Nikon D70s
    • Nikon D700
    • Nikon D5000
    • Nikon D80
    • Nikon D90
    • Pentax istDL2
    • Pentax istDL
    • Pentax istDS2
    • Pentax K10D
    • Pentax K100D
    • Pentax K200D
    • Pentax K-x
    • Samsung GX10
    • Canon EOS 1D Mark III
    • Canon EOS 1Ds Mark III
    • Canon EOS 1D Mark IV
    • Canon EOS 300D
    • Canon EOS 350D
    • Canon EOS 400D
    • Canon EOS 450D *
    • Canon EOS 500D *
    • Canon EOS 1000D *
    • Canon EOS 5D
    • Canon EOS 5D Mark II
    • Canon EOS 7D
    • Canon EOS 10D
    • Canon EOS 20D
    • Canon EOS 30D
    • Canon EOS 40D *
    • Canon EOS 50D *
    • Canon EOS D30
    • Canon EOS D60
    • Fuji S1 Pro
    • Fuji S2 Pro
    • Fuji S3 Pro
    • Fuji S5 Pro
    • Kodak DCS 14n
    • Kodak DCS Pro
    • Konica Minolta 7D
    • Nikon D1
    • Nikon D1h
    • Nikon D1x
    • Olympus E-1
    • Olympus E-3 *
    • Olympus E-10
    • Olympus E-20
    • Olympus E-300 *
    • Olympus E-330 *
    • Olympus E-400 *
    • Olympus E-410 *
    • Olympus E-500 *
    • Olympus E-510 *
    • Sigma SD-10
    • Sony Alpha 900

    * Im Absatz Was machen wenn die Kamera nicht unterstützt wird ? wird eine alternative Möglichkeit zum Bestimmen der Auslösungen beschrieben.

    Wie viele Auslösungen schafft meine Kamera

    Es gibt keine allgemeingültige Regel wie lange der Verschluss einer Kamera hält, das ist abhängig vom Modell, dem Glück, Temperatur und wohl auch etwas wie man mit seiner Kamera umgeht. Es gibt für einige Modelle ungefähre Richtwerte, die aber auch nur eine MTBF (Mean Time between Failures) darstellen und KEINE garantierte Anzahl an Auslösung (was immer wieder gerne von Leute in Foren behauptet wird) darstellen. Hier habe ich ein paar MTBF-Werte gesammelt, teilweise Werte die der Hersteller selbst angibt (gekennzeichnet durch einen roten * und verlinkt auf die Hersteller-Seite, woher die Information stammt), teilweise Werte die man öfters in (Test)Berichten, Foren und im Rest des Internets findet.

    Kamera Auslösungen
    Canon 300/350D/400D 50.000
    Canon 10D/20D 50.000
    Canon 30D 100.000 * Link (canon.com)
    Canon 40D 100.000 * Link (canon.com)
    Canon 5D 100.000 * Link (canon.com)
    Canon 5D Mark II 150.000 * Link (canon.com)
    Canon 7D 150.000 * Link (canon.com)
    Canon 1Ds 150.000 * Link (canon.com)
    Canon 1D Mark II 200.000 * Link (canon.com)
    Canon 1D Mark II-N 200.000 * Link (canon.com)
    Canon 1Ds Mark II 200.000 * Link (canon.com)
    Canon 1D Mark III 300.000 * Link (canon.com)
    Canon 1Ds Mark III 300.000 * Link (canon.com)
    Canon 1D Mark IV 300.000 * Link (canon.com)
    Nikon D3 300.000 * Link (nikon.de)
    Nikon D300 150.000 * Link (nikon.de)
    Nikon D700 150.000 * Link (nikon.com)
    Sony a900 100.000 * Link (sony.com)

    Unter www.olegkikin.com/shutterlife findet sich eine von Usern gepflegte Liste, wie viele Auslösungen ihre Kameras haben und ob sie noch funktionieren, aber wie vieles im Internet wohl nicht unbedingt so zuverlässig, aber vielleicht für den ein oder anderen ein netter Anhaltspunkt.

    Was kann ich machen wenn meine Kamera nicht unterstützt wird ?

    Wenn man Besitzer einer DSLR von Olympus ist (E-3 / E-300 / E-330 / E-400 / E-410 / E-500 / E-510) dann kann man sich die Informationen direkt an der Kamera selbst anzeigen lassen. Mehr Informationen dazu findet man bei Olypedia.de/Tastenkombination.

    Für einige Canon Kameras mit dem DIGIC III-Prozessor (40D / 50D / 450D / 1000D) gibt es für Windows und MAC-User ein Program namens 40D Shutter Count, das ein installiertes Canon EOS Utility vorraussetzt und darüber die Anzahl der Auslösungen direkt aus der Kamera auslesen kann (nicht aus den Exifs eines Bildes). 40D Shutter Count

    Bei anderen Kameras bleibt dann noch der Gang zum Hersteller oder einer Vertragswerkstatt des Händlers, die können das meistens als einzige genau und zuverlässig.

    Wie funktioniert das ganze ?

    Hinter der Fassade hier passiert nicht viel, das ausgewählte Bild wird in einen temporären Ordner hochgeladen. Über ExifTool (vexiftoolversion) wird versucht die Auslösungen auszulesen. Dann wird das Bild gelöscht und das Script gibt die Seite mit den ausgelesen Auslösungen aus.

    Sind die Zahlen zuverlässig ?

    Die ausgegebene Anzahl der Auslösungen ist nicht zuverlässig.
    Ich übernehme keinerlei Garantie für ihre Richtigkeit!
    Das liegt daran, dass es einige Methoden gibt die Zähler der Kameras zurückzusetzen, zu manipulieren und einige Wege, wie diese Werte unwissentlich verändert werden können.
    Ein paar mir bekannte Möglichkeiten:

    • Bei einigen Kameras kann das einspielen einer neuen Firmware reichen um den Zähler zurückzusetzen.
    • Bei einigen Kameras (manche Modelle der 1er-Serie von Canon) besteht die Möglichkeit die Kamera-Einstellungen der Kamera auf eine Speicherkarte zu speichern und später wieder zu laden, dabei wird der aktuelle Zählerstand mit gespeichert und beim Laden der Einstellungen wieder mit dem alten Wert überschrieben.
    • Bei einigen Kameras wird durch das entfernen der Pufferbatterie (oder wenn diese leer ist) der Zählerstand zurückgesetzt werden.
    • Bei einigen Herstellern kamen Kameras aus dem Service (Reperatur, Überprüfung oder ähnliches) zurück und waren auch zurückgesetzt.

    Warum wird meine Kamera nicht unterstützt ?

    Das kann verschiedene Gründe haben, entweder den Entwicklern von exiftool ist nicht bekannt wie und wo in den Exifs die Informationen stehen (Wenn du es herausgefunden hast, freuen Sie sich sicher über die Information) oder der Hersteller der Kamera hat aus irgendeinem Grund beschlossen diese Informationen nicht in den Exifs der Bilder zu speichern.

    Meine Kamera steht nicht in der Liste aber es werden trotzdem Auslösungen ausgelesen

    Dann würde ich mich freuen wenn du mich über mein Kontaktformular kontaktierst und mir mitteilen um welches Model es sich handelt, super wäre auch wenn du mir 1-2 Bilder zur Verfügung stellen kannst zum testen.

    Meine Kamera steht in der Liste aber es können keine Auslösungen ausgelesen werden

    Als erstes solltest du überprüfen, ob du auch wirklich ein unbearbeitetes JPEG/RAW-Bild direkt aus der Kamera genommen hast. Durch das erneute Speicher, verändern oder verkleinern mit einigen Programmen (beispielsweise: Photoshops für Web speichern), werden teilweise alle oder auch nur manche Informationen in den Exifs verändert, verschoben, umbenannt oder gelöscht, dann können die Informationen natürlich nicht mehr ausgelesen werden.

    Ich habe eine neue Kamera gekauft und der Zähler fing nicht bei 0 an

    Bei einigen Herstellern ist es wohl üblich, dass die Kameras vor dem Verkauf getestet werden und so schon teilweise ein paar 100 Auslösungen vorhanden sind, auch kann der Verkäufer selbst vielleicht die Kamera schon mal getestet haben um nicht ein defektes Gerät zu verkaufen.

    Welche Datei-Formate werden unterstützt ?

    Primär funktioniert JPEG,ie gängigen RAW-Formate der Kamera-Hersteller sollten wohl auch alle funktionieren, wurdem aber nicht getestet. Aufgrund der kleineren Größe (und der Möglichkeit sie noch weiter zu verkleinern vor dem Hochladen) sollten JPEGs daher bevorzugt werden, da sie meist kleiner sind und sich daher schneller hochladen lassen.

    Meine Bilder sind größer als das Uploadlimit, was kann ich tun ?

    Sollte das Bild größer als uploadLimit sein, musst es mit einem passenden Programm verkleinert werden das die Exifs nicht verändert oder löscht, unter Windows wäre das beispielsweise mit dem kostenlosen Bildbetrachter irfanview möglich.
    Alternativ kann man auch Exiftool selbst runterladen und damit selbst die Auslösungen auslesen, wie das funktioniert wird unter: Kann ich die Auslösungen auch offline auslesen ? beschrieben

    Kann ich die Auslösungen auch offline auslesen ?

    Exiftool kann auch Offline, direkt auf dem Rechner genutzt werden (Windows, Linux und Mac). Dazu müsst ihr euch nur auf der Homepage von exiftool die für euer Betriebssystem passende Version herunterladen und installieren.

    Exiftool selbst besitzt keine graphische Oberfläche, das heißt es muss über die Kommandozeile/Shell des Betriebsystems ausgeführt werden.

    cd /pfad/zum/bild/
    exiftool -shuttercount bild.jpg
    

    Wenn es dabei keine Ausgabe gibt, konnten keine Informationen ausgelesen werden.

    Für Nikon gibt es im DSLR-Forum vom User Georg M. das Tool NShutterCount.

    Unter welchem Betriebsystem funktioniert diese Seite ?

    Diese Seite sollte unabhängig vom verwendeten Betriebsystem funktionieren.

    Unter welchem Browser funktioniert diese Seite ?

    Diese Seite sollte mit jedem aktuellen Browser funktionieren der Dateiuploads unterstützt. JAVA oder Flash /werden nicht benötigt. Und Javascript nur ergänzend verwendet.

    Welche Version von Exiftool wird verwendet ?

    Aktuell wird Version exiftoolversion von exiftool verwendet.

    Meine Frage wird nicht von der FAQ beantwortet

    Wenn eure Fragen in der FAQ nicht beantwortet wird oder Kritik, Ideen, Vorschläge, habt, könnt ihr gerne das Kontaktformular verwenden um mir eine Nachricht zukommen zu lassen.

    ]]>
    Thu, 16 Jul 2009 12:00:00 +0000
    <![CDATA[Linux History Clean Script]]> http://www.robo47.net/codeschnipsel/8-Linux-History-Clean-Script http://www.robo47.net/codeschnipsel/8-Linux-History-Clean-Script Da für mich unter Linux die history der Shell sehr wichtig ist und die mit der zeit dann doch auch recht vollgemüllt mit gleichen einträgen ist, hab ich mir ein kleines PHP-Script gebastelt mit dem ich die History aufräumen kann ohne die reihenfolge oder ähnliches zu verändern.

    Während die mir bekannten Shell-Einzeiler mit uniq und sort alle die Reihenfolge innerhalb der History kaputt gemacht haben, hab ich mir ein kleines Script mit PHP gebastelt, das eine Datei einliest und jeweils das letzte vorkommen einer Zeile wieder in eine Datei schreibt, so bleibt die Reihenfolge innerhalb der history komplett erhalten.

    Das Script einfach irgendwo abspeichern und dann einen Symlink in einem Ordner innerhalb von PATH setzen, dann kann man es von überall aufrufen.

    usage: cleanfile [OPTION] FILE
    cleanHistory 0.2
    -h, --help Shows this help
    -d, --debug Shows debug information after cleaning the file


    #! /usr/bin/php
    <?php
    // Copyright (c) 2008-2009, Benjamin Steininger (Robo47)
    // All rights reserved.
    //
    // Redistribution and use in source and binary forms, with or without
    // modification, are permitted provided that the following conditions are met:
    //
    //     * Redistributions of source code must retain the above copyright notice,
    //      this list of conditions and the following disclaimer.
    //     * Redistributions in binary form must reproduce the above copyright
    //       notice, this list of conditions and the following disclaimer in the
    //       documentation and/or other materials provided with the distribution.
    //     * Neither the name of Robo47 nor the names of its contributors may be
    //       used to endorse or promote products derived from this software without
    //       specific prior written permission.
    //
    // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
    // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
    // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
    // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
    // THE POSSIBILITY OF SUCH DAMAGE.
    
    define('startTime', microtime(true));
    error_reporting(E_ALL | E_STRICT);
    
    define('CLEAN_HISTORY_VERSION',     '0.2');
    define('FILE_NOT_FOUND',            1);
    define('FILE_NOT_READABLE',         2);
    define('FILE_NOT_WRITEABLE',        3);
    define('NO_FILE',                   4);
    
    
    function showHelp($binary) {
        echo 'usage: ' . basename($binary) . ' [OPTION] FILE' . PHP_EOL;
        echo 'cleanHistory ' . CLEAN_HISTORY_VERSION . PHP_EOL;
        echo ' -h, --help   Shows this help' . PHP_EOL;
        echo ' -d, --debug  Shows debug information after cleaning the file' .
             PHP_EOL;
    }
    
    $debug = false;
    if ($argc > 3) {
        echo 'too many parameters';
        return 0;
    }
    $count = 1;
    foreach($argv as $key => $arg) {
        switch($arg) {
            case '--help':
            case '-h':
                showHelp($argv[0]);
                return 0;
            break;
            case '--debug':
            case '-d':
                if ($key == 1) {
                    $count++;
                }
                $debug = true;
            break;
        }
    }
    
    if (!isset($argv[$count])) {
        echo 'no file given' . PHP_EOL;
        return NO_FILE;
    }
    
    $sourceFile = $argv[$count];
    
    if (!file_exists($sourceFile)) {
        echo 'File not found: ' . $sourceFile;
        return FILE_NOT_FOUND;
    }
    if (!is_readable($sourceFile)) {
        echo 'File not readable: ' . $sourceFile;
        return FILE_NOT_READABLE;
    }
    if (!is_writeable($sourceFile)) {
        echo 'Unable to write to file: ' . $sourceFile;
        return FILE_NOT_WRITEABLE;
    }
    
    $beforeSize = filesize($sourceFile);
    
    // read sourcefile into array
    $aSource = file($sourceFile);
    
    // reverse array to filter
    $aSource = array_reverse($aSource);
    $aTarget = array();
    
    // add all new lines to thenew array if they arent already in
    foreach ($aSource as $line) {
        $line = trim($line);
        if (!isset($aTarget[$line]) && !empty($line)) {
            $aTarget[$line] = $line;
        }
    }
    
    // reverse array again for writing into file
    $aTarget = array_reverse($aTarget);
    
    file_put_contents($sourceFile, implode(PHP_EOL, $aTarget));
    
    if (true === $debug) {
        $sizeAfter = filesize($sourceFile) / 1024;
        $beforeSize = $beforeSize / 1024;
        $memUsage = memory_get_peak_usage() / 1024;
        echo 'Time: ' . round(microtime(true) - startTime, 3) . PHP_EOL;
        echo 'lines before: ' . count($aSource) . PHP_EOL;
        echo 'lines after:  ' . count($aTarget) . PHP_EOL;
        echo 'size before:  ' . round($beforeSize, 2) . 'kb ' . PHP_EOL;
        echo 'size after:   ' . round($sizeAfter, 2) . 'kb ' . PHP_EOL;
        echo 'mem-peak    : ' . round($memUsage, 0) . 'kb' . PHP_EOL;
    }
    return 0;
    ]]>
    Thu, 16 Jul 2009 12:00:00 +0000
    <![CDATA[Zip-Archiv von einem Ordner mit PHP erstellen und entpacken]]> http://www.robo47.net/codeschnipsel/9-Zip-Archiv-von-einem-Ordner-mit-PHP-erstellen-und-entpacken http://www.robo47.net/codeschnipsel/9-Zip-Archiv-von-einem-Ordner-mit-PHP-erstellen-und-entpacken Das Beispiel zeigt wie einfach man mit Hilfe des RecursiveDirectoryIterator und ZipArchiv ein Backup von einem Ordner erstellt und wieder entpackt.



    <?php
    $sourcePath = realpath('../../');
    
    $archiv = new ZipArchive();
    $archiv->open('archiv.zip', ZipArchive::CREATE);
    $dirIter = new RecursiveDirectoryIterator($sourcePath);
    $iter = new RecursiveIteratorIterator($dirIter);
    
    foreach($iter as $element) {
        /* @var $element SplFileInfo */
        $dir = str_replace($sourcePath, '', $element->getPath()) . '/';
        if ($element->isDir()) {
            // Ordner erstellen (damit werden auch leere Ordner hinzugefügt
            $archiv->addEmptyDir($dir);
        } elseif ($element->isFile()) {
            $file         = $element->getPath() .
                            '/' . $element->getFilename();
            $fileInArchiv = $dir . $element->getFilename();
            // Datei dem Archiv hinzufügen
            $archiv->addFile($file, $fileInArchiv);
        }
    }
    
    // Einen Kommentar mitspeichern
    $archiv->setArchiveComment('Backup von ' . $sourcePath);
    
    $archiv->close();
    
    // Und zum entpacken:
    
    $destinationPath = realpath('tmp/');
    $archiv = new ZipArchive();
    $archiv->open('archiv.zip');
    // In Ordner entpacken
    $archiv->extractTo($destinationPath);
    
    ]]>
    Thu, 16 Jul 2009 12:00:00 +0000
    <![CDATA[Html-E-Mail über SMTP via PEAR::Mail und PEAR::Mime versenden]]> http://www.robo47.net/codeschnipsel/7-Html-E-Mail-ueber-SMTP-via-PEAR-Mail-und-PEAR-Mime-versenden http://www.robo47.net/codeschnipsel/7-Html-E-Mail-ueber-SMTP-via-PEAR-Mail-und-PEAR-Mime-versenden Beispiel-Code zum versenden einer E-Mail über SMTP mit der Klasse PEAR::Mail PEAR::Mail_Mime und PEAR::Net_SMTP aus dem PHP Extension and Application Repository. Verwendete Versionen: Mail: 1.2.0b1, Mail_Mime: 1.5.2, Net_SMTP 1.3.2, PEAR: 1.7.2

    Anmerkung:

    Der Code von PEAR ist ursprünglich für PHP 4 geschrieben worden, daher kommt es mit error_reporting(E_ALL | E_STRICT); zu einigen Warnmeldungen betreffs der Strict Standards.

    Downloads:

    Entweder man nutzt den PEAR-Installer:
    pear install pear/Mail pear/Mail_Mime pear/Net_SMTP
    Oder man lädt sich die Pakete einzeln herunter:



    <?php
    $data = array();
    $data['smtp'] = array();
    $data['smtp']['host'] = 'mail.example.com';
    $data['smtp']['port'] = '25';
    $data['smtp']['username'] = 'username';
    $data['smtp']['password'] = 'password';
    
    $data['from'] = array('name' => 'Absender', 'email' => 'absender@example.com');
    $data['to'] = array('name' => 'Empfänger', 'email' => 'empfaenger@example.com');
    $data['charset'] = 'utf-8';
    $data['subject'] = 'Html Mail';
    
    $data['html'] = '
    <html>
        <head>
            <title>'.$data['subject'].'</title>
        </head>
        <body>
            <div>
                <h1>Meine Html-Mail</h1>
                <p>
                    Lorem ipsum dolor sit amet.
                </p>
            </div>
        </body>
    </html>';
    
    $data['text'] = 'Meine Html-Mail
    
    Lorem ipsum dolor sit amet.';
    
    require_once 'Mail.php';
    require_once 'Mail/mime.php';
    
    $options = array( 'auth'     => true,
                      'host'     => $data['smtp']['host'],
                      'port'     => $data['smtp']['port'],
                      'username' => $data['smtp']['username'],
                      'password' => $data['smtp']['password']);
    
    $mailer = Mail::factory('smtp', $options);
    
    $mail = new Mail_Mime(PHP_EOL);
    $mail->setHTMLBody($data['html']);                   // Html-Body
    
    $header = $mail->headers();
    $header['Subject']      = $data['subject'];         // Betreff setzen
    $header['from']         = $data['from']['email'];   // Absender setzen
    $header['Content-type'] = 'text/html';
    $header['Charset']     = 'UTF-8';
    
    $mailer->send($data['to']['email'], $header, $mail->get());
    ]]>
    Thu, 16 Jul 2009 12:00:00 +0000
    <![CDATA[Html-E-Mail über SMTP via phpmailer versenden]]> http://www.robo47.net/codeschnipsel/6-Html-E-Mail-ueber-SMTP-via-phpmailer-versenden http://www.robo47.net/codeschnipsel/6-Html-E-Mail-ueber-SMTP-via-phpmailer-versenden Beispiel-Code zum versenden einer E-Mail über SMTP mit der Klasse phpmailer. Verwendet wurde Version 2.3 des phpmailers.

    Downloads:



    <?php
    $data = array();
    $data['smtp'] = array();
    $data['smtp']['host'] = 'mail.example.com';
    $data['smtp']['port'] = '25';
    $data['smtp']['username'] = 'username';
    $data['smtp']['password'] = 'password';
    
    $data['from'] = array('name' => 'Absender', 'email' => 'absender@example.com');
    $data['to'] = array('name' => 'Empfänger', 'email' => 'empfaenger@example.com');
    $data['charset'] = 'utf-8';
    $data['subject'] = 'Html Mail';
    $data['html'] = '
    <html>
        <head>
            <title>'.$data['subject'].'</title>
        </head>
        <body>
            <div>
                <h1>Meine Html-Mail</h1>
                <p>
                    Lorem ipsum dolor sit amet.
                </p>
            </div>
        </body>
    </html>';
    
    $data['text'] = 'Meine Html-Mail
    
    Lorem ipsum dolor sit amet.';
    
    require_once '/class.phpmailer.php';
    
    $mail = new PHPMailer(true);        // true for throwing Exception on Problems
    
    // Server-Zugangsdaten setzen
    $mail->IsSMTP();
    $mail->IsHTML(true);                                // Als HTML-Mail senden
    $mail->SMTPAuth = true;
    $mail->set('Host', $data['smtp']['host']);
    $mail->set('Username', $data['smtp']['username']);
    $mail->set('Password', $data['smtp']['password']);
    $mail->set('Port', $data['smtp']['port']);
    $mail->set('Body', $data['html']);                  // HTML Nachricht setzen
    $mail->set('AltBody', $data['text']);               // Text Nachricht setzen
    $mail->set('CharSet', $data['charset']);            // Charset festlegen
    $mail->set('Subject', $data['subject']);            // Betreff setzen
    $mail->SetFrom($data['from']['email'], $data['from']['name']);
    $mail->AddAddress($data['to']['email'],
                      $data['to']['name']);             // Empfänger hinzufügen
    
    // Email absenden
    $mail->Send();
    ]]>
    Thu, 16 Jul 2009 12:00:00 +0000
    <![CDATA[Html-E-Mail über SMTP via Swiftmailer 3.3 versenden]]> http://www.robo47.net/codeschnipsel/5-Html-E-Mail-ueber-SMTP-via-Swiftmailer-3.3-versenden http://www.robo47.net/codeschnipsel/5-Html-E-Mail-ueber-SMTP-via-Swiftmailer-3.3-versenden Beispiel-Code zum versenden einer E-Mail über SMTP mit dem Swiftmailer. Verwendet wurde die Version 3.3.3 für php5.

    Download:

    Dieser Eintrag ist veraltet und funktioniert nur mit dem 3.3-Zweig von Swiftmailer, für den aktuellen 4.0er Zweig gibt es eine hier eine Anleitung: Html-E-Mail über SMTP via Swiftmailer 4.0 versenden



    <?php
    $data = array();
    $data['smtp'] = array();
    $data['smtp']['host'] = 'mail.example.com';
    $data['smtp']['port'] = '25';
    $data['smtp']['username'] = 'username';
    $data['smtp']['password'] = 'password';
    
    $data['from'] = array('name' => 'Absender', 'email' => 'absender@example.com');
    $data['to'] = array('name' => 'Empfänger', 'email' => 'empfaenger@example.com');
    $data['charset'] = 'utf-8';
    $data['subject'] = 'Html Mail';
    
    $data['html'] = '
    <html>
        <head>
            <title>'.$data['subject'].'</title>
        </head>
        <body>
            <div>
                <h1>Meine Html-Mail</h1>
                <p>
                    Lorem ipsum dolor sit amet.
                </p>
            </div>
        </body>
    </html>';
    
    $data['text'] = 'Meine Html-Mail
    
    Lorem ipsum dolor sit amet.';
    
    require_once 'Swift.php';
    require_once 'Swift/Connection/SMTP.php';
    
    // Verbindung erstellen
    $smtp = new Swift_Connection_SMTP($data['smtp']['host'], $data['smtp']['port']);
    $smtp->setUsername($data['smtp']['username']);
    $smtp->setPassword($data['smtp']['password']);
    
    // Mail erstellen
    $mail = new Swift_Message($data['subject']);
    $mail->setBody($data['html']);
    $mail->setCharset($data['charset']);
    $mail->setContentType("text/html");
    
    // Absender angeben
    $mail->setFrom(new Swift_Address($data['from']['email'],
                                     $data['from']['name']));
    
    $recipient = new Swift_Address($data['to']['email'],
                                   $data['to']['name']);
    // Mail versenden
    $swift = new Swift($smtp);
    $swift->send($mail,$recipient , $data['from']['email']);
    ]]>
    Thu, 16 Jul 2009 12:00:00 +0000
    <![CDATA[Einfacher Dateiupload mit PHP und Curl]]> http://www.robo47.net/codeschnipsel/10-Einfacher-Dateiupload-mit-PHP-und-Curl http://www.robo47.net/codeschnipsel/10-Einfacher-Dateiupload-mit-PHP-und-Curl Dieses kleine Beispiel soll zeigen wie man mit cURL einen einfachen Datei-Upload an ein Formular realisiert.

    Alternativ stehen auch noch Code-Schnipsel mit Zend_Http_Client und Snoopy zur Verfügung.



    <?php
    $url = 'http://www.domain.tld/pfad/zum/formular.php';
    
    // Array mit den Feldern des Formulars und den zu sendenden Werten
    $form = array();
    // Pfad zur hochzuladenden Datei mit einem @ davor
    $form['userfile'] = '@./bla.pdf';
    // Weiteres Felder das im Formular vorkommt
    $form['name'] = 'cURL';
    
    // cURL Instanz erstellen
    $curl = curl_init($url);
    
    // Angeben dass es ein POST-Request ist
    curl_setopt($curl, CURLOPT_POST, 1);
    // Die zu übermittelnden Felder angeben
    curl_setopt($curl, CURLOPT_POSTFIELDS, $form);
    
    // Anfrage abschicken und antwort in $response speichern
    $response = curl_exec($curl);
    
    // cURL Instanz beenden
    curl_close($curl);
    
    // Jetzt muss man nur noch $response auswerten und schauen ob der Upload
    // erfolgreich war, das lässt sich je nach Ausgabe auf viele verschiedene Arten
    // lösen strpos(), Regulären Ausdrücken (preg_match), DOM und Xpath oder
    // ähnlichem.
    ]]>
    Thu, 16 Jul 2009 12:00:00 +0000
    <![CDATA[Einfacher Dateiupload mit PHP und Zend_Http_Client]]> http://www.robo47.net/codeschnipsel/11-Einfacher-Dateiupload-mit-PHP-und-Zend_Http_Client http://www.robo47.net/codeschnipsel/11-Einfacher-Dateiupload-mit-PHP-und-Zend_Http_Client Dieses kleine Beispiel soll zeigen wie man mit Zend_Http_Client einen einfachen Datei-Upload an ein Formular realisiert.

    Alternativ stehen auch noch Code-Schnipsel mit cURL und Snoopy zur zur Verfügung.



    <?php
    require_once 'Zend/Loader.php';
    Zend_Loader::registerAutoload();
    
    $url = 'http://www.domain.tld/pfad/zum/formular.php';
    
    // Instanz
    $client = new Zend_Http_Client($url);
    // Datei angeben die man hochladen will
    $client->setFileUpload('./bla.pdf', 'userfile');
    // Ein weiteres Feld
    $client->setParameterPost('name', 'Zend');
    
    // Anfrage abschicken
    $response = $client->request(Zend_Http_Client::POST);
    
    $body     = $response->getBody();
    
    // Jetzt muss man nur noch $body auswerten und schauen ob der Upload
    // erfolgreich war, das lässt sich je nach Ausgabe auf viele verschiedene Arten
    // lösen strpos(), Regulären Ausdrücken (preg_match), DOM und Xpath oder
    // ähnlichem.
    ]]>
    Thu, 16 Jul 2009 12:00:00 +0000
    <![CDATA[Ordnergröße, Anzahl Dateien, Ordner und Symlinks mit PHP und der RecursiveDirectoryIterator auszulesen]]> http://www.robo47.net/codeschnipsel/15-Ordnergroese-Anzahl-Dateien-Ordner-und-Symlinks-mit-PHP-und-der-RecursiveDirectoryIterator-auszulesen http://www.robo47.net/codeschnipsel/15-Ordnergroese-Anzahl-Dateien-Ordner-und-Symlinks-mit-PHP-und-der-RecursiveDirectoryIterator-auszulesen Script um mit RecursiveDirectoryIterator und SplFileInfo die Ordnergröße, Anzahl Dateien, Ordner und Symlinks auszulesen.
    Beispielausgabe:

    Anzahl Elemente:      1822
    Anzahl Dateien:       1439
    Anzahl Ordner:        376
    Anzahl Symlinks:      7
    Größe der Dateien:    19709595
    


    <?php
    error_reporting(E_ALL | E_STRICT);
    $dirIter = new RecursiveDirectoryIterator('./');
    $recursiveIterator = new RecursiveIteratorIterator($dirIter, 
                                        RecursiveIteratorIterator::SELF_FIRST,
                                        RecursiveIteratorIterator::CATCH_GET_CHILD);
    
    $counts = array();
    $counts['files']        = 0;
    $counts['links']        = 0;
    $counts['directorys']   = 0;
    $counts['all']          = 0;
    $counts['size']         = 0;
    
    foreach($recursiveIterator as $element)
    {
        /* @var $element SplFileInfo */
        switch($element->getType())
        {
            case 'file':
                $counts['files']++;
                $counts['size'] += $element->getSize();
                break;
            case 'link':
                $counts['links']++;
                break;
            case 'dir':
                $counts['directorys']++;
                break;
        }
        $counts['all']++;
    }
    
    echo 'Anzahl Elemente:      ' . $counts['all'] . PHP_EOL;
    echo 'Anzahl Dateien:       ' . $counts['files'] . PHP_EOL;
    echo 'Anzahl Ordner:        ' . $counts['directorys'] . PHP_EOL;
    echo 'Anzahl Symlinks:      ' . $counts['links'] . PHP_EOL;
    echo 'Größe der Dateien:    ' . $counts['size'] . PHP_EOL;
    ]]>
    Thu, 16 Jul 2009 12:00:00 +0000