Par un détour à Yoda

{ }

recherche

Aller au contenu | Aller au menu | Aller à la recherche

PHP bench: isset vs array_key_exists

I tested the rapidity of isset vs array_key_exists. I always thougt that array_key_exists would be the winner ...

Here is my script :

┌─(yoda@ev5)(14:25:29)
└─(~/var/www/test)-> cat isset_array-key-exists.php 
#!/usr/bin/php
<?php
 
define('N', "\n");
 
error_reporting(E_ALL | E_NOTICE);
 
$nbLoop = 10000;
 
$testArray = array('foo' => 'bar', 'bar' => 'quux');
 
$time = array();
 
$desc = array(
    0 => 'array_key_exists on existant key',
    1 => 'array_key_exists on non existant key',
    2 => 'isset on existant key',
    3 => 'isset on non existant key',
);
 
 
/**
 * begin with array_key_exists, existant key
 */
$time[0]['start'] = microtime(true);
 
for($i = 0; $i < $nbLoop; $i++) {
    array_key_exists('foo', $testArray);
}
 
$time[0]['stop'] = microtime(true);
 
 
/**
 * begin with array_key_exists, non existant key
 */
$time[1]['start'] = microtime(true);
 
for($i = 0; $i < $nbLoop; $i++) {
    array_key_exists('foz', $testArray);
}
 
$time[1]['stop'] = microtime(true);
 
 
 
/**
 * begin with isset, existant key
 */
$time[2]['start'] = microtime(true);
 
for($i = 0; $i < $nbLoop; $i++) {
    isset($testArray['foo']);
}
 
$time[2]['stop'] = microtime(true);
 
 
/**
 * begin with isset, non existant key
 */
$time[3]['start'] = microtime(true);
 
for($i = 0; $i < $nbLoop; $i++) {
    isset($testArray['foz']);
}
 
$time[3]['stop'] = microtime(true);
 
 
$differences = array();
echo 'results: '.N;
foreach($time as $k => $t) {
    echo 'test '.$k.' "'.sprintf('%40s', $desc[$k]).'" : '.($difference[$k] = $t['stop'] - $t['start']).N;
}

And here is the results :

┌─(yoda@ev5)(14:25:32)
└─(~/var/www/test)-> php isset_array-key-exists.php 
results: 
test 0 "        array_key_exists on existant key" : 0.00795984268188
test 1 "    array_key_exists on non existant key" : 0.00665903091431
test 2 "                   isset on existant key" : 0.00737690925598
test 3 "               isset on non existant key" : 0.00131511688232

And the winner is : isset !

PHP bench: count + for vs foreach vs while

I tested different loops. Here is my test script :

┌─(yoda@ev5)(14:32:05)
└─(~/var/www/test)-> cat for_foreach_while.php 
#!/usr/bin/php
<?php
 
define('N', "\n");
 
error_reporting(E_ALL | E_NOTICE);
 
$nbLoop = 10000;
 
$testArray = array_fill(0, 500, 'toto');
 
$time = array();
 
$desc = array(
	'iterating an array with a count + for',
	'iterating an array with a foreach',
	'iterating an array with a while',
);
 
 
/**
 * iterating with for
 */
echo 'Starting test 1'.N;
$time[0]['start'] = microtime(true);
 
for($i = 0; $i < $nbLoop; $i++) {
	$nb = count($testArray);
	for($j = ($nb - 1); $j > 0; $j--) {
	}
}
 
$time[0]['stop'] = microtime(true);
echo 'Test 1 ended'.N;
 
/**
 * iterating with foreach
 */
echo 'Starting test 2'.N;
$time[1]['start'] = microtime(true);
 
for($i = 0; $i < $nbLoop; $i++) {
	foreach($testArray as $k => $v) {
	}
}
 
$time[1]['stop'] = microtime(true);
echo 'Test 2 stopped'.N;
 
 
/**
 * iterating with while(list() = each())
 */
echo 'Starting test 3'.N;
$time[2]['start'] = microtime(true);
 
for($i = 0; $i < $nbLoop; $i++) {
	while(list($k, $v) = each($testArray)) {
	}
	reset($testArray);
}
 
$time[2]['stop'] = microtime(true);
echo 'Test 3 ended'.N;
 
 
$differences = array();
echo 'results: '.N;
foreach($time as $k => $t) {
    echo 'echo test '.$k.' "'.sprintf('%40s', $desc[$k]).'" : '.($difference[$k] = $t['stop'] - $t['start']).N;
}

And the results are :

┌─(yoda@ev5)(14:32:47)
└─(~/var/www/test)-> php for_foreach_while.php 
Starting test 1
Test 1 ended
Starting test 2
Test 2 stopped
Starting test 3
Test 3 ended
results: 
echo test 0 "   iterating an array with a count + for" : 0.489103078842
echo test 1 "       iterating an array with a foreach" : 0.750061988831
echo test 2 "         iterating an array with a while" : 4.91836500168

The winner is count+for, followed by foreach, and while is far behind. I suppose it's because of the reset.

PHP: 5.2.12 (debian) in CLI

Special thanks to Folliked for remembering me that while still exists ;)

Symfony: Validate fields depending of another field (update 2010-02-23)

It's possible, when validating a form, to check if a field is correct dependently of an other field's value.

First, in your configure method, after the declaration of your validators, add :

$this->validatorSchema->setPostValidator(
    new sfValidatorCallback(
        array('callback' => array($this, 'checkRequiredFields'))
    )
);

And create a new method in your form class !

public function checkRequiredFields($validator, $values) {
    $errors = array();
    /**
     * checking if mandatory_field == 3, then other_field must be filled
     */
    if(3 == $values['mandatory_field'] && null == $values['other_field']) {
        $error = new sfValidatorError($validator, 'if you choose the option 3 for mandatory_field, you need to fill the other_field');
        $errors['other_field'] = $error;
        //throw new sfValidatorErrorSchema($validator, array('other_field' => $error));
    } else {
        // nothing to check
    }
 
 
    /**
     * checking if foo is selected, then bar must be filled
     */
    if(1 == $values['foo'] && null == $values['bar']) {
        $error = new sfValidatorError($validator, 'if you check foo, you need to fill bar');
        $errors['bar'] = $error;
    } else {
        // nothing to check
    }
 
    /**
     * more depency checking here
     */
 
    /**
     * if there is errors
     */
    if(count($errors)) {
        throw new sfValidatorErrorSchema($validator, $errors);
    }
    /**
     * dont forget to return values !
     */
    return $values;
}

2010-02-23 Updated: All errors are now returned once. 2010-02-23 Updated: Deleted the commented throw, added another check.

Symfony: change the default type of your primary keys

Hey, yes it's possible to change the default type of your primary keys with a simple configuration

Open your config/databases.yml. It should looks like this :

 [yml]
# You can find more information about this file on the symfony website:
# http://www.symfony-project.org/reference/1_4/en/07-Databases

all:
  doctrine:
    class: sfDoctrineDatabase
    param:
      dsn:      mysql:host=localhost;dbname=.
      username: root
      password:

But Doctrine lets you customize some things. Now the file looks like :

 [yml]
# You can find more information about this file on the symfony website:
# http://www.symfony-project.org/reference/1_4/en/07-Databases

all:
  doctrine:
    class: sfDoctrineDatabase
    param:
      dsn:      mysql:host=localhost;dbname=.
      username: root
      password:
      attributes:
        default_identifier_options:
          type: integer
          length: 4
          unsigned: true

Nowyou can change the parameter, lets say an integer, and length: 5.

It's event possible to add any configuration that will be added automatically to your model.

Note: It's not possible to set autoincrement: false. True is forced by doctrine. Note: This behavior is true when no primary column is found in your schema.

Thanks to PhilG (#symfony-fr @ Freenode) for testing. Tested with symfony 1.4.1

Symfony, tasks and return values

In symfony, it's possible to return a value in your execute() method.

Let's create a test task :

┌─(yoda@box)(09:46:59)
└─(~/var/www/test)-> ./symfony generate:task test
>> task      Creating "/home/yoda/var/www/test.../testTask.class.php" task file

Adding a return value at the end of the method. :

<?php
 
class testTask extends sfBaseTask
{
  protected function configure()
  {
    // // add your own arguments here
    // $this->addArguments(array(
    //   new sfCommandArgument('my_arg', sfCommandArgument::REQUIRED, 'My argument'),
    // ));
 
    $this->addOptions(array(
      new sfCommandOption('application', null, sfCommandOption::PARAMETER_REQUIRED, 'The application name'),
      new sfCommandOption('env', null, sfCommandOption::PARAMETER_REQUIRED, 'The environment', 'dev'),
      new sfCommandOption('connection', null, sfCommandOption::PARAMETER_REQUIRED, 'The connection name', 'doctrine'),
      // add your own options here
    ));
 
    $this->namespace        = '';
    $this->name             = 'test';
    $this->briefDescription = '';
    $this->detailedDescription = <<<EOF
The [test|INFO] task does things.
Call it with:
 
  [php symfony test|INFO]|>
EOF;
  }
 
  protected function execute($arguments = array(), $options = array())
  {
    // initialize the database connection
    $databaseManager = new sfDatabaseManager($this->configuration);
    $connection = $databaseManager->getDatabase($options['connection'] ? $options['connection'] : null)->getConnection();
 
    // add your code here
    return 42;
  }
}

And now, execute the task :

┌─(yoda@box)(09:47:32)
└─(~/var/www/test)-> ./symfony test
┌─(yoda@box)(09:47:40)
└─(~/var/www/test)-> echo $?
42

The return value is correctly returned. Note that without return value, the task simply returns 0.

In the case that an exception occurs, let's see what is returned. I added a dummy exception in the execute method :

<?php
 
class testTask extends sfBaseTask
{
  protected function configure()
  {
    // // add your own arguments here
    // $this->addArguments(array(
    //   new sfCommandArgument('my_arg', sfCommandArgument::REQUIRED, 'My argument'),
    // ));
 
    $this->addOptions(array(
      new sfCommandOption('application', null, sfCommandOption::PARAMETER_REQUIRED, 'The application name'),
      new sfCommandOption('env', null, sfCommandOption::PARAMETER_REQUIRED, 'The environment', 'dev'),
      new sfCommandOption('connection', null, sfCommandOption::PARAMETER_REQUIRED, 'The connection name', 'doctrine'),
      // add your own options here
    ));
 
    $this->namespace        = '';
    $this->name             = 'test';
    $this->briefDescription = '';
    $this->detailedDescription = <<<EOF
The [test|INFO] task does things.
Call it with:
 
  [php symfony test|INFO]|>
EOF;
  }
 
  protected function execute($arguments = array(), $options = array())
  {
    // initialize the database connection
    $databaseManager = new sfDatabaseManager($this->configuration);
    $connection = $databaseManager->getDatabase($options['connection'] ? $options['connection'] : null)->getConnection();
 
    // add your code here
    throw new Exception('foo');
    return 42;
  }
}
┌─(yoda@box)(09:51:37)
└─(~/var/www/test)-> ./symfony test

               
  foo         
               

┌─(yoda@box)(09:51:43)
└─(~/var/www/test)-> echo $?
1

The task returns 1 in case of an exception.

[Symfony] Overwrite a value in a form

It happen sometimes that you have to set/override a value within a form but without using a hidden field (that can be overridden)

Here is a short solution to set the user id in the object we are going to save.

Let's say that we have an object PersonnalData linked to the sfGuardUserProfile

In your PersonnalDataForm.class.php :

class PersonnalDataForm extends BasePersonnalDataForm {
 
    public function configure() {
    }
 
    public function processValues($values = null) {
        $values['user_id'] = $this->getOption('user_id');
        return parent::processValues($values);
    }
 
 
}

$this->getOption('user_id') is a mechanism to pass variables from outside the form to it. To set it, you must use the second argument of the form's constructor.

In your action :

public function executeMyaction(sfWebRequest $request) {
        //                                             setting an array containg the variable we want to use in our form
        $this->form = new PersonnalDataForm(null, array('user_id' => $this->getUser()->getProfile()->getId() ));
 
        if($request->isMethod('post')) {
            $this->form->bind($request->getParameter($this->form->getName()));
            if($this->form->isValid()) {
                $this->form->save();
            } else {
                //echo 'not valid';
            }
        }
    }

[Symfony] Load an sql dump wihtin a task

I recently had a problem when loading fixtures in my symfony project. I was loading cities, about 37 000 of them, and the doctrine:data-load was extremly slow (about 5 minutes). I finally found a solution to load an sql dump wihtin a task, using the doctrine/default connection.

With this solution, I reduced the time from minutes to 2 secondes.

<?php
 
class loadALotOfDataTask extends sfBaseTask
{
  protected function configure()
  {
    // // add your own arguments here
    // $this->addArguments(array(
    //   new sfCommandArgument('my_arg', sfCommandArgument::REQUIRED, 'My argument'),
    // ));
 
    $this->addOptions(array(
      new sfCommandOption('application', null, sfCommandOption::PARAMETER_REQUIRED, 'The application name', 'frontend'),
      new sfCommandOption('env', null, sfCommandOption::PARAMETER_REQUIRED, 'The environment', 'dev'),
      new sfCommandOption('connection', null, sfCommandOption::PARAMETER_REQUIRED, 'The connection name', 'doctrine'),
      // add your own options here
    ));
 
    $this->namespace        = '';
    $this->name             = 'loadALotOfData';
    $this->briefDescription = '';
    $this->detailedDescription = <<<EOF
The [loadALotOfData|INFO] task does things.
Call it with:
 
  [php symfony loadALotOfData|INFO]|>
EOF;
  }
 
  protected function execute($arguments = array(), $options = array())
  {
    // initialize the database connection
    $databaseManager = new sfDatabaseManager($this->configuration);
    $connection = $databaseManager->getDatabase($options['connection'] ? $options['connection'] : null)->getConnection();
 
    // add your code here
    $file = '../data/dump.sql';
    if(!file_exists($file)) {
        $this->logSection('File', sprintf('File %s does not exists', $file));
        return;
    }
    // loading the file in the memory. In my case, the file was 
    $data = file_get_contents($file);
    // passing queries to the orm
    $statement = $connection->prepare($data);
    // executing
    $statement->execute();
  }
}

[PHP] Pensez à STDClass

Il arrive souvent qu'on ai besoin de retourner plusieurs informations distinctes dans une fonction ou une méthode. La plupart du temps, il suffit de retourner un tableau indexé de manière intelligente.

class CSS {
    public function getDeclaration($pDeclaration) {
        list($key, $value) = explode(':', $pDeclaration);
        $key = trim($key);
        $value = trim($value, ' ;');
        return array('key' => $key, 'value' => $value);
    }
};
$ret = CSS::getDeclaration('background-color: #FFFFFF;');
echo 'Clé: '.$ret['key'];
echo 'Valeur: '.$ret['valeur'];

Mais quand on utilise des frameworks tel que symfony où tout (ou presque) est objet, il devient difficile de chainer les éléments.

STDClass[1] vient donc à notre secours.

class CSS {
    public function getDeclaration($pDeclaration) {
        list($key, $value) = explode(':', $pDeclaration);
        $key = trim($key);
        $value = trim($value, ' ;');
        $r = new STDClass;
        $r->key = $key;
        $r->value = $value;
        return $r;
    }
};

Il devient maintenant possible de faire une déclaration du genre :

echo 'Clé: '.CSS::getDeclaration('background-color: #FFFFFF;')->key;
echo 'Valeur: '.CSS::getDeclaration('text-decoration: underline;')->value;

Update :

Petite mise à jour, j'avais fait une petite typo dans le premier bout de code, au niveau des echo-s. Merci Neolitec.

Notes

[1] http://php.net/stdclass

Asus 1005HA-H and Debian

I recently recevied my new Asus eee1005HA-H bough on Materiel.net. Everything seemed fine as there was a Debian project supporting the eee-s. But unfortunately the current installer couldn't support networking devices.

Lire la suite

My first plugin for symfony

Hey, I just uploaded my first plugin for symfony !

This plugin is a simple wrapper for a jQuery Carousel found at http://sorgalla.com/projects/jcarousel/

Display lastest dotclear posts in symfony

Here is a little tip to display the latest posts of your dotclear blog in your symfony website.

Create a connection

If your blog database is in an other database than symfony's one, you need to create a connection in the /config/databases.yml. Otherwise, skip this step Mine looks like this :

[yml]
dev:
  beta:
    param:
      classname:  DebugPDO

test:
  beta:
    param:
      classname:  DebugPDO

all:
  symfony:
    class:        sfDoctrineDatabase
    param:
      classname:  DoctrinePDO
      dsn:        mysql:dbname=mysymfonydbanem;host=localhost
      username:   mysymfonyusername
      password:   mysymfonypassword
      encoding:   utf8
      persistent: true
      pooling:    true
  blog:
    class:        sfDoctrineDatabase
    param:
      classname:  DoctrinePDO
      dsn:        mysql:dbname=myblogdbname;host=localhost
      username:   myblogusername
      password:   myblogpassword
      encoding:   utf8
      persistent: true
      pooling:    true

Read more about connections.

Create Dotclear models from the database

You must create models from the database to be able to retrieve the posts

$ ./symfony doctrine:build-schema --env=blog
$ ./symfony doctrine:build-model

As you can see, some Dc*.class.php are now created in the /lib/model/doctrine/ folder.

Fetching latests posts

In your controller (actions.class.php), use and customize this request :

public function executeIndex(sfWebRequest $request)
  {
    $this->posts = Doctrine_Query::create()
        ->from('DcPost p')
        ->where('p.post_status = 1')
        ->orderBy('p.post_creadt DESC')
        ->limit(5)
        ->execute();
  }

Displaying posts

<?php foreach($posts as $post) : ?>
  <div class="blog_box">
    <h5><a href="#"><?php echo $post->getPostTitle() ?></a></h5>
    <?php echo __('Posted by') ?> <a href="http://url.to.your.blog/path/index.php?post/<?php echo $post->getPostUrl() ?>"><span><?php echo $post->getDcUser()->getUserDisplayname () ?></span></a> <?php echo __('in') ?> <a href="#"><span><?php echo $post->getDcCategory()->getCatTitle() ?></span></a><br />
    <?php echo substr(strip_tags($post->getRawValue()->getPostContentXhtml()), 0, 40).'...' ?><br />
    <div class="datetime"><?php echo $post->getPostCreadt() ?></div>
  </div>
<?php endforeach; ?>

and that's it, you made it ! :)

PS: This howto is maybe somehow wrong, I made those steps few times before making this working. Feel free to correct.

Update 1 : Added a where clause to select only posts where post_status = 1. Added the link to the post

Protect your .svn files in your website

If you are using like me svn to manage your website, you certainly notified that all the .svn files are fully readable by everyone in plain text. So here is a fix to deny the acces to those files :

<Directory ~ "\.svn">
    Order allow,deny
    Deny from all
</Directory>

Solution found at http://alexking.org/blog/2005/04/12/svn-as-a-web-site-maintenence-tool

Howto reveal an input type="password" with simple tools

When your browser is configured to save password fields, it's very easy to reveal that field without knowing the master password (because *of course* you have a master password).

Just use firebug. Load the page, and change change <input type="password" /> by <input type="text" />

HADOPI - Le Net en France : black-out

HADOPI - Le Net en France : black-out

Decibel audio player and MP3

I recently installed a fresh debian lenny. To play some music I decided to keep decibel audio player that I already have on my personal pc.

But when I wanted to play some MP3's it kept saying:

** Message: don't know how to handle audio/mpeg, mpegversion=(int)1, layer=(int)3
** Message: don't know how to handle audio/mpeg, mpegversion=(int)1, layer=(int)3
** Message: don't know how to handle audio/mpeg, mpegversion=(int)1, layer=(int)3
** Message: don't know how to handle audio/mpeg, mpegversion=(int)1, layer=(int)3

I finally found why. You just have to install gstreamer0.10-fluendo-mp3

Hope this help

Toussa toussa

Notify plugin for aMSN updated (1.4)

Here is a new version of my plugin.

  • Don't show the event if the user is blocked (and the "don't show blocked user notification" config is set
  • Added OS Check
  • Code cleaning
  • Bumped to version 1.4

Thanks to KaKaRoTo for the blocked system.

MPD Remote control script v0.2

Here is a new version of my console mpd remote script.

Lire la suite

Recomposer un HTML valide après l'avoir tronqué.

Récemment, je me suis occupé de la création d'un site web tout joli tout beau, dans lequel les webmasters peuvent composer certaines parties de leur site web via un éditeur online wysiwyg. Mais à certains endroits, j'avais besoin de mettre juste le début du texte suivi des traditionnels 3 petits points.

Lire la suite

Notify plugin for aMSN updated (1.3)

New version of my notify plugin for aMSN

Changelog:

Download the pugin: notify-plugin-by-Yoda-BZH_2008-10-19.tar.bz2

See my previous post for more information on this plugin.

- page 1 de 13