Tag Archives: php

Named function parameters in PHP

Officially, PHP doesn’t support named function parameters. But there is one easy way to emulate this feature – so easy that it doesn’t really matter that this feature is missing. It’s actually a pretty old trick inspired from Javascript I’ve been using since forever. I thought everyone knew it, but I’m still surprised by how many people come up with overly complicated solutions. Anyway, imagine you have a method that have a lot of optional parameters. Without named parameters, it goes like this:

// $queue is required, the rest is optionnal
function bind_queue($queue, $durable = false, $auto_delete = true, $name = false, $answer = null)
{ 
...
}

Later in your code, if you want to specify another value for $answer, you have to copy the values of every intermediate parameters in your function call. It’s time consuming, error prone and confusing.

// Good luck remember which parameter is what. 
bind_queue($my_queue, false, true, false, 42);

So instead, it’s much better to use an associative array, like this:

// leave the required parameter part of the function definition, the options goes into an array
function bind_queue($queue, array $opt = array())
{
  // we define the default values, and merge with the user values at the same timle
  $opt = array_merge(array(
    'durable' => false,
    'auto_delete' => true,
    'name' => false,
    'answer' => null
  ), $opt);

  // use $opt['answer'] to access the 'answer' optionnal parameter
}

Later in your code, if you want to override answer, you can only write this:

bind_queue($my_queue, ['answer' => 42]);

Much easier to read, isn’t it?

Bypassing PHP’s open_basedir with MySQL

PHP feature open_basedir is supposed to limit the files that can be opened by PHP to a specified directory-tree (full doc is here). Functions like fopen or file_get_contents will returns an error if the file is outside the allowed directory. So far, it sounds like a good protection.

However, it is also very famous for being flawed by design and easy to violate (like safe_mode by the way). Well, until now I didn’t realize how easy it is indeed to bypass it. During a security audit, I add the opportunity to study a backdoor left here by some script kiddie (thanks to an outdated version of a web application). Here is just one interesting example that uses MySQL:

  1. create a temporary table
  2. use MySQL’s command LOAD DATA INFILE to read any file and load is content to the table
  3. select the content of the table

In clean PHP, the code would looks like:

$filename = '/etc/passwd';

$pdo = new PDO($dsn, $username, $password);
$pdo->exec('CREATE TEMPORARY TABLE tmp_file ( content LONGBLOB NOT NULL)');
$pdo->exec(sprintf(
    'LOAD DATA INFILE %s INTO TABLE tmp_file',
    $pdo->quote($filename)
));
$content = $pdo->query('SELECT * FROM tmp_file')->fetchAll(PDO::FETCH_COLUMN);

To prevent that exploit in particular, it’s easy: just make sure that the MySQL’s user doesn’t have the FILE privilege. But open_basedir is definitely not safe. As seen in Debian’s php.ini default file:

This is considered a “broken” security measure. Applications relying on this feature will not recieve full support by the security team

Implementation of “tail -f” in PHP

This is a small algorithm to implement a functionality similar to “tail -f” in PHP. The script is able watch a file in real time and do something everytime a new line is added (for example, a log file). It doesn’t implement the “tail” functionnality however (outputing only the end of the file) and instead starts processing the file from the beginning.

$file = @ fopen($filename, 'r');
$pos = 0; 

while (true) {
    fseek($file, $pos);
    while ($line = fgets($file)) {
        // do something with $line
    }
    $pos = ftell($file);
    sleep(1);
}
fclose($file);

Use destructor carefully

A couple of days ago I lost half a day of work on a stupid bug in a PHP CLI script. Basically, the script wouldn’t die. Instead it would hang forever, eating all the CPU time. Even explicitly calling die() (or exit(), since they are synonyms) wouldn’t terminate it. Yep, that’s right, die() didn’t work! Until then, I naively believed that die() was some failsafe language construct that would terminate the script not matter what. But as it turned out, it’s not the case…

According to PHP documentation: The destructor will be called even if script execution is stopped using exit(). So destructor’s code will be executed even after die() is called. What happens if a destructor’s code is faulty and gets stuck in an infinite loop ? The script will never end. In my story, I was using an third-party object-oriented library (for AMQP) that did some funky stuff in some objects destructor, and then waited forever for an event on the network… Needless to say, it’s a BAD idea to write that much application logic in a destructor.

So the moral of the story is: (1) die() can fail and (2) use destructor with caution, only write code that will NEVER fail and that is STRICLY necessary like closing connections, closing file handlers and such. Same goes for shutdown functions by the way.

A quick example for the sake of demonstration:

class Evil
{
    public function __destruct()
    {
        while (true);
    }
}

$foo = new Evil();
die();

N’utilisez pas isset pour vérifier l’existence d’une clef !

La bonne blague PHP du jour concerne la fonction isset qui permet, comme son nom l’indique, de tester si une variable est affectée. Si vous êtes comme moi, vous avez pris l’habitude de l’utiliser pour vérifier qu’une certaine clef existe dans un tableau avant d’y accéder, afin d’éviter le traditionnel message “Notice: Undefined index”.

Seulement voila, isset retourne false si la variable contient null… Exemple :

$array = array(
    'toto' => null
);
var_dump(isset($array['toto'])); // boolean false

Résultat : on n’obtient pas du tout le résultat attendu, puisque selon PHP la clef n’est pas définie alors que selon le programmeur (moi, en l’occurence) l’index toto existe bien ! D’après la documentation, il faut donc utiliser array_key_exists pour tester qu’une clef existe…

Bon, que PHP considère qu’une variable définie mais de valeur null et qu’une variable non-définie soit la même chose, c’est déjà un peu curieux, mais là où ça devient complètement ridicule c’est que isset renverra bien true pour une chaine vide (ainsi que pour false), alors que c’est équivalent à null avec l’opérateur de comparaison non-typé == ! Alors pourquoi isset n’a pas le même comportement pour toutes ces valeurs ?

$array = array(
    'toto' => null,
    'tata' => ''
);

var_dump(isset($array['toto'])); // boolean false
var_dump(isset($array['tata'])); // boolean true
var_dump($array['toto'] == $array['tata']); // boolean true

Les fetch modes de PDO 4 : les modes modificateurs

Dernière partie de ma série consacrée aux modes de récupération de PDO, voici les modes que j’appelle “modificateurs”, car ils ne peuvent pas être utilisés tout seuls et se contentent de modifier le comportement d’un autre mode.

Pour utiliser un modificateur, il suffit d’effectuer un “ou” binaire, par exemple :

$results->fetchAll(PDO::FETCH_TRUC | PDO::FETCH_BIDULE | PDO::FETCH_CHOUETTE);

La plupart de ces modes ne fonctionnent pas lorsqu’ils sont définis par la méthode setFetchMode. Ça ressemble a un bug, mais vu que rien n’est documenté, c’est peut-être normal…

Continue reading

Les fetch modes de PDO

PDO propose une quantité assez impressionante de “modes de récupération” (fetch mode) des données. Entendez par là qu’il est possible de personnaliser le comportement des méthodes fetch et fetchAll pour obtenir les résultats des requêtes SQL sous des formes très diverses. Si certains modes sont relativement classiques et ont des équivalents avec les précédents drivers MySQL (mysql_fetch_array, mysql_fetch_row, etc.), d’autres sont totalement inédits… mais très mal documentés ! Et pourtant, certains sont très pratiques… J’ai donc décidé d’écrire une série d’articles présentant tous les modes de fetch, leur fonctionnement et surtout leurs performances.

Continue reading

Cannot re-assign $this (en théorie)

Décidemment le modèle objet de PHP n’est pas mon meilleur ami. Après avoir eu la suprise de découvrir que certaines méthodes ne peuvent pas lancer d’exception, voila que $this change mystérieusement de valeur.

Comme le suggère le dollar dans le nom, $this est bien une simple variable. Donc à ce titre elle peut surement être modifiée, non ? Heureusement, la changer par une assignation directe est impossible, et conduira à une erreur fatale. Ainsi, le code suivant affichera l’erreur Cannot re-assign $this.

class ThisIsFun {
    function reassignThis()
    {
        $this = 'something else';
    }
}

$obj = new ThisIsFun();
$obj->reassignThis();

Mais certaines fonctions, comme extract peuvent aussi manipuler les variables (je l’utilise pour exporter certaines variables vers la vue dans le cadre d’une architecture MVC). Résultat, avec un code comme ci-après, $this est modifié…

class ThisIsFun {
    function reassignThis($vars)
    {
        var_dump($this); // object(ThisIsFun)[1]
        extract($vars);
        var_dump($this); // string 'something else' (length=14)
    }
}

$obj = new ThisIsFun();
$obj->reassignThis(array('this' => 'something else'));

Heureusement la modification n’est effective que dans le contexte de la fonction, mais quand même, c’est plutôt étonnant que ça soit possible. Pour l’éviter il faudrait utiliser le paramètre optionnel supplémentaire extract_type qui contrôlle la gestion des collisions.

Du coup je me suis amusé un peu à chercher d’autres moyens de modifier $this, même si je ne sais pas encore trop à quoi ça pourrait bien servir… Allez, avec une référence c’est élégant :)

class ThisIsFun {
    function reassignThis()
    {
        var_dump($this); // object(ThisIsFun)[1]
        $that = & $this;
        $that = 'references are cool';
        var_dump($this); // string 'references are cool' (length=19)
    }
}

$obj = new ThisIsFun();
$obj->reassignThis();