Mots-clé : php

Développement : Coding contest !

Challenge de codeurs !

C’est pour le fun, essayez vous aussi 

Le principe est simple

Vous aurez deux puzzles de programmation à résoudre.

Pour ce faire, vous pourrez choisir votre langage favori parmi ceux-ci : Java, C, C++, C#, Javascript, PHP, Python, Ruby, Objective-C, Haskell and Go.
La durée moyenne d’un challenge est de 2 heures et 30 minutes.

J’y serai, le 28 septembre !

Php : une boucle qui incarne une des pires horreurs possibles

Voici une horreur qu’on peut trouver, en plein milieu du code sur lequel on m’a demandé de travailler.
Si vous faites mieux dans la série « la pire horreur en Php », n’hésitez pas à en donner un exemple en commentaire…

for ($i=0; $i<count($tab); $i++)
{
    if ($tab[key($tab)]==true) {
        $montableau2[][key($tab)]='code';
    }
    next($tab);
}

Php : challenge ASCII art

J’ai fait un pari. Petit challenge à réaliser : faire un script (peu importe le langage) qui prend en paramètre un fichier image, et qui en sort de l’ASCII art.
Il faut qu’il soit rapidement paramétrable sur la taille des fontes.

Je l’ai fait « théorique » en 59 minutes, mais entièrement fonctionnel en une heure trente. J’ai fait deux scripts : un qui génère le fichier des fontes, et le principal qui lit le fichier image des fontes, et le fichier image en paramètre, et sort de l’ASCII art.

Exemple de résultat (si si c’est bien du texte, vous pouvez le sélectionner et copier coller en tant que texte !) :

BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBMBBBBBBBBBBBB
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBB¨ MBBBBBBMM0BBB
BBBBBBBBBBBBBBBBBBBBBBBBBP'`BB  ,BBBP¨    MBB
BBBBBBBBBBBBBBBBBBBBBBBBB&  %BMBPBBL  ,a  ¨BB
BBBBBBBBBBBBBBBBBBBBBBBBBB  jBB  MBBaBBP   BB
BBBBBBBBBBBBBBBBBBBBBBBBPM   BB  ¨BBB^  ,  0B
BBBBBBBBBBBBBBBBBBBBBBP`     BBb  BB'  mB  jB
BBBBBBB@P'BBBBBB@M@BBB   mB  $BK  0B  jBP  ¨B
BBBBBBBK  BBBP^`   MBB  dBB  MBB  jB   ¨ L,aB
B^¨ 'BBB  %BB  ,a   BB  &BBL  BB   Bh  ,WBBBB
B    ¨BB  jBB,mBM^  $B  'BBF  BBL  BBBBBBBBBB
BL    `BL  BBBP¨    jB   ^^   %B&mBBBBBBBBBBB
B&  w  ¨&  BBP  wB   B&    wamBBBBBBBBBBBBBBB
BB  %w     MBL  BB¨  BBBwamBBBBBBBBBBBBBBBBBB
BB  MBw    ¨BK  ¨`, ,&BBBBBBBBBBBBBBBBBBBBBBB
BBL  BBN    BB,  aBBBBBBBBBBBBBBBBBBBBBBBBBBB
BB&  BBBh   0BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
BBB  MBBBhwmBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
BBB  OBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB

Ne regardez pas la suite si vous voulez essayer de le faire pour vous !

<?php
function usage_et_exit($message = '', $exit_error = -1) {
    global $argv;
    if ($message != '') {
        echo $message."\n";
    }
    echo $argv[0]." {image gif reference} {image noir et blanc jpg}\n";
    exit($exit_error);
}

function noir_ou_blanc($r, $g, $b) {
    /* http://stackoverflow.com/questions/254388/how-do-you-convert-an-image-to-black-and-white-in-php */
    if (((0.299*$r) + (0.587*$g) + (0.114*$b))> 0x7F) {
        return 0xFFFFFF;
    } else {
        return 0x000000;
    }
}

function cherche_meilleure_correspondance(
    $img_src, $x, $y, $img_fonts, $font_size, $tab_base
) {
    $meilleur = -1;
    $retour = '';
    foreach ($tab_base as $idx => $ch) {
        $match = 0;
        for ($t_y=0; $t_y<$font_size; $t_y++) {
            for ($t_x=0; $t_x<$font_size; $t_x++) {
                if (imagecolorat($img_src, $t_x+$x, $t_y+$y)
                    != imagecolorat(
                        $img_fonts,
                        ($idx*$font_size) + $t_x, $t_y
                    )
                ) {
                    $match++;
                }
            }
        }
        if ($match>$meilleur) {
            $retour = $ch;
            $meilleur = $match;
        }
    }
    return $retour;
}

function convert_img_noir_et_blanc($filename, $img)
{
    list($width, $height, $type, $attr) = getimagesize($filename);
    /* Conversion en noir et blanc */
    for ($y=0; $y<$height; $y++) {
        for ($x=0; $x<$width; $x++) {
            $rgb = imagecolorat($img, $x, $y);
            $r = ($rgb >> 16) & 0xFF;
            $g = ($rgb >> 8) & 0xFF;
            $b = $rgb & 0xFF;
            $nb = noir_ou_blanc($r, $g, $b);
            imagesetpixel($img, $x, $y, $nb);
        }
    }
}

$tab_base = array(
    '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
    'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c',
    'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q',
    'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4',
    '5', '6', '7', '8', '9', '&', '\'', ',', ';', ':', '?', '.', '/', '!',
    '%', '^', '¨', '$', '~', '#', '(', ')', '[', ']', '|', '-', '`', ' '
);
$font_size = 14;
$min_width  = 400;
$min_height = 400;

if ($argc!=3) {
    usage_et_exit("Arguments invalides", -1);
}

$img_tmp = imagecreatefromstring(file_get_contents($argv[1]));
$width = imagesx($img_tmp);
$height = imagesy($img_tmp);
$img_fonts = imagecreatetruecolor($width, $height);
imagecopy($img_fonts, $img_tmp, 0, 0, 0, 0, $width, $height);
imagedestroy($img_tmp);
unset($img_tmp);
if ($img_fonts===false) {
    usage_et_exit("Erreur chargement de ".$argv[1], -1);
}

/* Conversion en noir et blanc des fontes */
convert_img_noir_et_blanc($argv[1], $img_fonts);
imagepng($img_fonts, './resultat_fonts.png', 0);

list($width, $height, $type, $attr) = getimagesize($argv[2]);
if ($width<$min_width) {
    usage_et_exit("Largeur trop petite de ".$argv[1].", taille min = ".$width_min, -1);
}
if ($width<$min_height) {
    usage_et_exit("Hauteur trop petite de ".$argv[1].", taille min = ".$width_min, -1);
}

$img_src = imagecreatefromjpeg($argv[2]);
if ($img_src===false) {
    usage_et_exit("Erreur chargement de ".$argv[2], -1);
}
convert_img_noir_et_blanc($argv[2], $img_src);
/* Ok on a l'image en noir et blanc. Parcourir toute l'image */
imagepng($img_src, './resultat_noir_et_blanc.png', 0);

$y = 0;
$max_result = 0;
while (($y + $font_size) < $height) {
    $x = 0;
    while (($x + $font_size) < $width) {
        echo cherche_meilleure_correspondance(
            $img_src, $x, $y, $img_fonts, $font_size, $tab_base
        );
        $x += $font_size;
    }
    echo "\n";
    $y += $font_size;
}
echo "Done.\n";
?>

Et le fichier qui génère les fontes (22 minutes de perdues à le faire…) :

<?php
$tab_base = array(
    '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
    'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c',
    'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q',
    'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4',
    '5', '6', '7', '8', '9', '&', '\'', ',', ';', ':', '?', '.', '/', '!',
    '%', '^', '¨', '$', '~', '#', '(', ')', '[', ']', '|', '-', '`', ' '
);
$x=0;
$font_size = 14;
$width = count($tab_base)*$font_size;
echo 'width='.var_export($width,true)."\n";
$height = $font_size*4;
$img = imagecreatetruecolor($width, $height);
if ($img===false) {
    throw new Exception("Erreur");
}
imagefilledrectangle($img, 0, 0, $width, $height, 0xFFFFFF);
foreach ($tab_base as $idx => $char) {
    $ok = imagettftext(
        $img,
        $font_size, 0,
        $idx * $font_size, $font_size,
        0x000000,
        './consola.ttf', $char
    );
}
imagegif($img, './test.gif');
echo "Done.\n";
?>

PHPExcel : comment générer des cellules avec des styles différents

PHPExcel est un outil pour génerer des fichier Excel en Php.

Il y a beaucoup d’exemples, et tout est bien censé fonctionner. Comme les méthodes sont très parlantes et que c’est bien écrit, j’ai voulu faire un code qui crée plusieurs cellules avec des styles différents.

Ce qui n’est pas expliqué dans la documentation c’est ce qui suit : les « pages » Excel (« tabsheets ») ont des groupes de styles, et chaque cellule référence un de ces styles (sachant qu’il y a un style par défaut). Donc ce que j’ai fait était faux : j’ai essayé d’appliquer directement un style à une cellule, puis un autre style à une autre cellule. Le code exécuté ne correspondait pas du tout à ce que je pensais. Le code faisait ceci : je recherche le style à appliquer parmi tous les styles qui sont recensés dans le « tabsheet » courant. Comme le style n’existait pas, alors il remplaçait le style par défaut du « tabsheet » courant par celui que je demandais à appliquer. Donc comme toutes les cellules référençaient le même style, elles avaient toutes les mêmes propriétés, et évidemment, la dernière « application de style » modifiait en conséquence toutes les cellules. La solution est simple, mais elle n’est pas documentée, et de plus, la fonction à appeler (« duplicateStyle() ») n’est pas très parlante, contrairement à la totalité de l’outil PhpExcel (ce qui est d’autant plus déroutant).

Voici mon code, et je vous explique après pourquoi ça ne fonctionnait pas, et ce qu’il faut faire pour que cela fonctionne.

(1) Je crée deux tableaux différents, de styles différents :

$styleParDefaut = array(
    'font' => array(
        'name'   => 'Bernard MT Condensed',
        'size'   => 10,
        'italic' => false,
        'bold'   => false,
    ),
    'alignment' => array(
        'horizontal' => PHPExcel_Style_Alignment::HORIZONTAL_LEFT,
        'wraptext' => false
    )
);
$styleDonneesTelechargeesLe = array(
    'font' => array(
        'name'   => 'Arial',
        'size'   => 10,
        'italic' => true,
        'bold'   => false,
    ),
    'alignment' => array(
        'horizontal' => PHPExcel_Style_Alignment::HORIZONTAL_LEFT,
        'wraptext' => false
    )
);

(2) Puis je tente de les assigner, un par cellule, comme ça je pensais obtenir deux cellules avec deux styles différents :

function setText($sheet, $colonne, $ligne, $style, $text)
{
    $cell->setValue($text);
    $cell = $sheet->getCellByColumnAndRow($colonne, $ligne);
    $cell->getStyle()->applyFromArray($style);
    $cell->setValue($text);
}

Et le code par cellule :

setText(
    $sheet,
    $colonne, $ligne + 3,
    $styleParDefaut,
    'Texte 1'
);
setText(
    $sheet,
    $colonne, $ligne + 5,
    $styleDonneesTelechargeesLe,
    'Texte 2'
);

Eh bien ça ne fonctionne pas du tout comme cela. En lisant le code, il faut générer dynamiquement les style, et les passer via duplicateStyle().

Donc :

(1) Créer les styles :

$styleParDefaut = new PHPExcel_Style();
$styleParDefaut->applyFromArray(array(.. même chose que précedemment ..));
$styleDonneesTelechargeesLe = new PHPExcel_Style();
$styleDonneesTelechargeesLe->applyFromArray(array(.. même chose que précedemment ..));

(2) Les réutiliser via duplicateStyle()

$cell = $sheet->getCellByColumnAndRow($colonne, $ligne);
$sheet->duplicateStyle($style, $cell->getCoordinate());
$cell->setValue($text);

Symfony 2 et Doctrine et repository : faire un leftJoin avec createQueryBuilder

Comment utiliser createQueryBuilder() ?

La documentation n’est pas très claire sur le sujet. Enfin, disons que si vous êtes comme moi, il va vous manquer des exemples pour mieux comprendre. Je vais essayer de vous faire gagner du temps.

Voilà le problème : j’ai crée un repository pour mes partenaires, que j’ai appelé PartenaireRepository.php.

Dans la plupart des exemples, ils utilisent createQueryBuilder('p'), qui semble pratique (puisqu’il référence immédiatement la table et en fait un alias (dans mon exemple c’est p)

J’ai donc voulu utiliser createQueryBuilder() mais j’ai eu besoin deux jointures d’affilée : les partenaires avaient une ou plusieurs adresses, et ces adresses étaient réliées à des villes. La solution est en fait simple, à partir du moment où on a compris le principe :

    class PartenaireRepository extends EntityRepository
    {
        /**
         * Récupération de tous les partenaires donnés pour un
         * code postal donné.
         */
        public function findAllActiveByCp($cp)
        {
            return $this->createQueryBuilder('p')
                ->leftJoin('p.adresses', 'a')
                ->leftJoin('a.ville', 'v')
                ->where('v.cp=:cp')
                ->setParameter('cp', $cp);
        ... blabla
        }
    }

Ici, le leftJoin('p.adresses', 'a') signifie : dans la classe Partenaire que j’ai déclarée dans le fichier Entity\Partenaire.php, il y a la propriété adresses et tu vas faire une jointure dessus, et cette jointure, tu vas l’aliaser "a". On aura donc, à partir de cette jointure, une référence à une table adresse qu’on pourra utiliser via le "a".

Il est possible de refaire une jointure avec cet alias !

La preuve : la jointure juste après : ->leftJoin('a.ville', 'v') qui signifie, exactement sur le même principe : dans le fichier Entity\Adresse.php, il y a la propriété "ville" et tu vas faire une jointure dessus, et cette jointure, tu vas l’aliaser "v".

Enfin, je termine sur le "where" classique :

->where('v.cp=:cp').

D’après ce que j’ai compris, on ne peut faire des jointures que sur des propriétés qui sont elles même déclarées en tant que jointures. Donc, sur mon fichier « entité » Partenaire, ma jointure est déclarée ainsi :

/**
 * @ORM\ManyToMany(targetEntity="Adresse")
 * @ORM\JoinTable(name="partenaire_adresse",
 *      joinColumns={@ORM\JoinColumn(name="id_partenaire", referencedColumnName="id")},
 *      inverseJoinColumns={@ORM\JoinColumn(name="id_adresse", referencedColumnName="id")}
 *      )
 */
private $adresses;

Et de la même façon, sur mon fichier « entité » Ville, ma jointure est déclarée ainsi :

/**
 * @var string
 *
 * @ORM\ManyToOne(targetEntity="Ville")
 * @ORM\JoinColumn(name="id_ville", referencedColumnName="id")
 */
private $ville;

J’espère vous avoir fait gagner du temps, parce que pour moi, la syntaxe n’était pas évidente à trouver.

Symfony: Could not open input file: composer.phar solution

Si jamais vous avez ce problème, une solution qui peut fonctionner est de lancer un shell, et d’aller dans votre répertoire source.
De là, lancez cet ordre :

curl -s http://getcomposer.org/installer | php

Et tout rentrera dans l’ordre !

Drupal : code pour les liens avec des URLs dynamiques

Drupal : comment s’adapter à une migration si le site n’est plus à la racine ?

Dans les modules, activation du module « Php filter », afin que le code Php soit exécuté.
Et dans le texte qu’il y a dans un bloc « menu », par exemple, vous pourriez vous inspirer d’un code comme celui-ci (sachant qu’il vous faut absolument revoir l’indentation, qui n’est pas acceptable pour un code digne de ce nom) 🙂  :

<?php /* Ce code sert à générer les URLs
 * de manière dynamique, afin que si l'installation
 * Drupal est dans un sous-répertoire, les liens
 * soient "automatiquement" adaptés, via
 * le code "'absolute' => TRUE", 
 */ ?>
<ol>
    <li>
        <?php echo l(t('Menu1'),
            'lien1',
            array( 'absolute' => TRUE, 'query' => array()
        )); ?>
    </li>
    <li>
        <?php echo l(t('Menu2'),
            'lien2',
            array( 'absolute' => TRUE, 'query' => array()
        )); ?>
    </li>
    <li>
        <?php echo l(t('Menu3'),
            'lien3',
            array( 'absolute' => TRUE, 'query' => array()
        )); ?>
    </li>
    <li>
        <?php echo l(t('Menu4'),
            'lien4',
            array( 'absolute' => TRUE, 'query' => array()
        )); ?>
    </li>
</ol>

Cours optimisation Internet – Ecole d’ingénieurs des Mines

Après avoir fait ma présentation, cela fait toujours plaisir de savoir qu’on est suffisamment intéressé pour demander ma présentation !

Cliquez sur le lien pour la récupérer, et n’hésitez pas à laisser une appréciation sur ce qui était bien et ce qui manquait éventuellement, sachant que j’ai fait ce que je pouvais dans un laps de temps aussi court 😉

Cliquez ici.

PhpDocumentor 2 : howto et résultats exceptionnels

Après plusieurs échanges avec le développeur principal de PhpDocumentor 2, celui-ci a corrigé plusieurs bogues et maintenant la version alpha tourne, et j’ai pu la lancer sur mon framework.

Résultats tout simplement exceptionnels (pas pour mon framework, mais pour PhpDocumentor).

Ce qui m’a le plus bluffé c’est le schéma de diagramme de classes : non seulement il est beau et pratique, mais il est clair.

Donc une vue globale de mon diagramme de classes :

Image petite du diagramme de classes

Et quand on clique pour zoomer, le rendu est tout aussi exceptionnel :

Image petite du zoom du diagramme de classes

C’est pratique, grâce à ça, je vois tout ce qui manque. Par exemple, les ListeXXX ne sont pas dans une classe générique, alors que les ItemXXX oui, des petites évolutions à faire pour rendre un peu tout ça plus propre, mais c’est un bon début !

Ensuite, viennent les classes et la documentation générée : de la même façon, si tout est correctement organisé, le rendu est vraiment très bon et la documentation est enfin utilisable !

Image petite de l'exemple de documentation de classes

Php : day of week – jour de la semaine. Astuce

Oui, on peut facilement imaginer plusieurs choses :

Les jours de la semaine sont numérotés de 1 à 7. Faux.

Ils sont numérotés de 0 à 6.

Enfin pour l’astuce qui pourrait certainement vous faire gagner du temps :
Les jours de la semaine commencent à lundi. Faux.

Si on lit la documentation : 0 (pour dimanche) à 6 (pour samedi).

Bon à savoir !