json_encode transforme les tableaux (arrays) en objets

Lors d’un développement sous VueJS, l’application présentait des problèmes de filtre (.filter) et de recherche (.find) d’un tableau récupéré en Ajax par vue-resource. Après examen, le tableau n’était plus un array d’objets mais un objet d’objets.

La faute en reviens a une subtilité de json_encode à la frontière du bug: si un tableau a des index discontinus (suite à un filtrage par exemple) celui-ci se trouve encodé en objet d’objets. Or, la fonction filter des collections de Laravel provoquent précisément ce défaut.

La solution c’est l’usage de array_values pour ré-indexer le tableau avant d’en faire un json.

exemple:

$tableau=collect([
    ["prenom"=>"Zoe","age"=>10],
    ["prenom"=>"Robert","age"=>50],
    ["prenom"=>"John","age"=>30]
    ]);

echo json_encode($tableau);
$tableau_filtre=$tableau->filter(function($element){return $element["age"]<40;});
echo json encode($tableau_filtre);

// resolution:
echo json_encode(array_values($tableau_filtre->toArray()));

=> ceci démontre le problème et sa résolution

Récupérer les dimensions d’un fichier PDF en PHP

Stockage de l’information de dimension

Les dimensions d’un fichier PDF sont stockées dans une unité nommé le « point pica » ou DTP .
Pour résumer, un point pica correspond à 1/72 de pouce. Pour plus d’information, lire ce lien

Conversion de la taille de point pica en millimètre

Un pouce correspond à 25,4 mm

La conversion en millimètre s’obtient donc en multipliant la taille en point pica par 25,4/72
donc, par exemple, si dans un fichier PDF on trouve une taille de 595×842

cela donnera:
595×25,4/72 = 209,90 mm
842×25,4/72 = 297,04 mm

=> Ce qui correspond a un format A4 portrait.

On observera que selon le logiciel utilisé pour la génération du fichier, on aura un variation dans la précision de cette mesure. de même, certains logiciels ajoute derrière ces mesures le format normé (A4 ou autre) correspondant et d’autres non. D’où la difficulté de l’opération.

D’autre part un fichier PDF peut comporter des pages de différentes tailles et/ou orientations.

Récupération de ces information depuis PHP

PHP peut récupérer ces informations via différents outils Linux. Certains donnent une dimension globale, bien souvent la taille de la 1ère page, d’autres au contraire sont capable de vous donner la taille page par page.
Dans presque tout les cas, il faudra filtrer le résultat pour récupérer les informations de hauteur et de largeur.

Plusieurs solutions sont possibles, j’ai choisi pdfinfo pour sa vélocité et ses fonctionnalités.

pdfinfo s’installe avec la commande

sudo apt-get install poppler-utils

ce logiciel, lancé sans parametre renvoi par défaut la taille de la 1ère page ainsi que d’autres informations dont le nombre de pages.
exemple:

pdfinfo test.pdf

nous renvoi:

Title: fichier de test
Subject:
Keywords:
Author: D.FERET
Creator: PDFCreator Version 0.9.9
Producer: GPL Ghostscript 8.70
CreationDate: Fri Sep 27 14:24:08 2013
ModDate: Fri Sep 27 14:24:08 2013
Tagged: no
Form: none
Pages: 3
Encrypted: no
Page size: 595 x 842 pts (A4)
Page rot: 0
File size: 33923 bytes Optimized: no PDF version: 1.4

Ce qui est insuffisant car on récupère que la taille de la 1ère page. Pour demander des informations plus détaillées, on spécifiera la plage de pages a analyser ( donc de 1 à -1 puisque, normalement, on ne connait pas a l’avance le nombre de pages du fichier ):

pdfinfo test.pdf -f 1 -l -1

nous renvoi:

Title: fichier de test
Subject:
Keywords:
Author: D.FERET
Creator:        PDFCreator Version 0.9.9
Producer:       GPL Ghostscript 8.70
CreationDate:   Fri Sep 27 14:24:08 2013
ModDate:        Fri Sep 27 14:24:08 2013
Tagged:         no
Form:           none
Pages:          3
Encrypted:      no
Page    1 size: 595 x 842 pts (A4)
Page    1 rot:  0
Page    2 size: 595 x 842 pts (A4)
Page    2 rot:  0
Page    3 size: 595 x 842 pts (A4)
Page    3 rot:  0
File size: 33923 bytes
Optimized: no
PDF version: 1.4

On observe que les résultats utiles sont noyés dans le texte et il va devoir les extraire grâce a des expressions régulières pour garantir le résultat.

Le script PHP

Au début du script, on lancera la commande avec la commande exec qui permet de récupérer le résultat dans un tableau $lignes:

// série de constante pour constituer plus facilement nos expressions régulières.
CONST ER_SEP = '.*?'; // Séparateur (espace et autre)
CONST ER_FLOAT = '([+-]?\\d*\\.?\\d+)(?![-+0-9\\.])';  // chiffre a virgule ou entier
CONST ER_ENTIER = '(\\d+)';  //nombre entier
CONST ER_ENTIERSIGNE = '([+-]?\\d+)'; //nombre entier avec ou sans signe + ou -

function getDimensionsPDF($nom_du_fichier_pdf){
   $commande = escapeshellcmd('pdfinfo "' . $nom_du_fichier_pdf . '" -f 1 -l -1');
   $lignes = array(); exec($commande,$lignes);

Ensuite 3 cas seront possibles:
La ligne comportant la dimension ET la taille normé
La ligne comportant la dimension SANS la taille normé
La ligne comportant l’information de rotation.

On aura donc besoin de 3 expressions régulières pour récupérer les informations. On passera en revu chaque ligne pour savoir si elle répondent à l’une des 3 expressions.

    // composition des 3 expressions régulières permettant de récupérer les lignes utiles.
    $expRegLigneAvecFormatNorme = "/^Page " . ER_SEP . ER_ENTIER . ER_SEP ."size:" . ER_SEP . ER_FLOAT . ER_SEP . ER_FLOAT . ER_SEP . "pts". ER_SEP . $Format . "$/";
    $expRegLigneSansNorme = "/^Page " . ER_SEP . ER_ENTIER . ER_SEP . "size:" . ER_SEP . ER_FLOAT . ER_SEP . ER_FLOAT . ER_SEP . "pts$/";
    $expRegLigneRotation = "/^Page " .  ER_SEP . ER_ENTIER . ER_SEP . "rot:" . ER_SEP . ER_ENTIERSIGNE . "$/";$pages = [];
    foreach ($lignes as $ligne) {

        if (preg_match($expRegLigneAvecFormatNorme, $ligne, $resultats)) { // ligne avec format normé en bout de ligne
        list(,$numeroPage,$largeur,$hauteur,$format)=$resultats;
        $pages[$numeroPage]["Largeur"] = $this->pts2mm($largeur);
        $pages[$numeroPage]["Hauteur"] = $this->pts2mm($hauteur);
        $pages[$numeroPage]["Format"] = trim($format, '()');

    } elseif (preg_match($expRegLigneSansNorme, $ligne, $resultats)) { // ligne sans format normé
        list(,$numeroPage,$largeur,$hauteur)=$resultats;
        $pages[$numeroPage]["Largeur"] = $this->pts2mm($largeur);
        $pages[$numeroPage]["Hauteur"] = $this->pts2mm($hauteur);

    } elseif (preg_match($expRegLigneRotation, $ligne, $resultats)) { // ligne contenant le numéro de page et la rotation
        list(,$numeroPage,$rotation)=$resultats;
        $pages[$numeroPage]["Rotation"] = $rotation;
    }    
 }
 return $pages;
}

function pts2mm($valeur)
{ //petite fonction qui converti un taille de pts pica en mm
    if (is_numeric($valeur)) {
        return round((float)$valeur * 25.4 / 72);
    }
    return 0;
}

Voila, j’espère que cette méthode pourra vous être utile. A bientôt.

Compter pages couleurs et noires d’un PDF

Voici une routine qui devrait être utile à tout les développeurs d’applications pré-presse. Le principe peut-être retranscrit dans n’importe quel langage et s’appuie sur l’outil ghostscript.

<?php
/**
 * Created by Dominique FERET.
 * Date: 26/08/2014
 * Time: 09:56
 */
/**
 * Fonction qui analyse la nature des pages d'un pdf.
 * @param $nomfichier   => chemin et nom du fichier.
 * @return $this        => retourne un tableau encodé en JSON contenant la liste des pages de chaque type et leur comptes
 */
function analysepdf($nomfichier)
{
    header('content-type: application/json');
    mb_internal_encoding("UTF-8");

    $o = new stdClass();
    if (file_exists($nomfichier)){
        $commande = 'gs  -o - -q -sDEVICE=inkcov "' . $nomfichier . '"';
        $lignes = array();
        exec($commande, $lignes);
        $noir = 0;
        $couleur = 0;
        $blanche = 0;
        $pagesblanches=array();
        $pagesnoires=array();
        $pagescouleurs=array();
        $numeropage=0;
        foreach ($lignes as $ligne) {

            $cyan = substr($ligne, 1, 7);
            $magenta = substr($ligne, 10, 7);
            $yellow = substr($ligne, 19, 7);
            $black = substr($ligne, 28, 7);
            if (is_numeric($cyan)&& is_numeric($magenta) && is_numeric($yellow) && is_numeric($black)){ // si la ligne contient bien une ligne de valeurs
                $numeropage++; // on ajoute 1 a la ligne en cours
                if((($cyan+$magenta+$yellow)==0)){
                    $noir++;
                    if($black==0) {
                        $pagesblanches[]=$numeropage;
                        $blanche++;
                    }
                    $pagesnoires[]=$numeropage;
                }else{
                    $couleur++;
                    $pagescouleurs[]=$numeropage;
                }
            }

        }
        $o->listepagescouleurs=implode(",",$pagescouleurs);
        $o->listepagesnoires=implode(",",$pagesnoires);
        $o->listepagesblanches=implode(",",$pagesblanches);
        $o->totalpages = $numeropage;
        $o->pagesnoires = $noir;
        $o->pagescouleurs = $couleur;
        $o->pagesblanches = $blanche;
    }
    return json_encode($o);
}

Recherche des jours ouvrés en PHP

calendrierIl est fréquent d’avoir besoin de calculer J+1 ou J-1 dans un logiciel mais que le résultat soit un jour ouvré.
Voici un listing permettant les calculs nécessaire. Dans ce listing, les jours ouvré sont considérés comme du Lundi au Vendredi. Il est possible de changer cela en ligne 70 sur array(0,6)

 

<?php
/*
Calcules de dates.
Ecrite par Dominique FERET le 17 Juin 2014

fonction paques(annee) => renvoi la date du dimanche de paques basé sur l'algorythme de Gauss.
fonction ferie(annee) => renvoi un tableau de tout les jours férié de l'année
fonction trouvejourouvre(date,decalage) => renvoi le prochain jour ouvré dans le sens du décalage (exprimé en jours)

exemple d'utilisation
echo trouvejourouvre("02-01-2014",-1)."<br>";
=> renverra 31-12-2013

echo trouvejourouvre("15-08-2014",1)."<br>";
=> renverra 18-08-2018 => le prochain jour ouvré après le 15 aout 2014 sera le 18 Aout.

echo trouvejourouvre("17-07-2014",-3)."<br>";
=> renverra 11-07-2014 => le jour ouvré j-3 du 17 juillet sera le 11 car j-3 = 14 (le 12 et 13 étant samedi dimanche)

*/

function paques($annee){
    $a=$annee%19;
    $b=$annee%4;
    $c=$annee%7;
    $d=(19*$a+24)%30;
    $e=(2*$b+4*$c+6*$d+5)%7;
    $j=22+$d+$e;
    $m=3;
    if($j>31){
        $m+=1;
        $j-=31;
    }
    $datepaques=sprintf("%02d-%02d-%04d",$j,$m,$annee);
    return $datepaques;
}
function ferie($annee){
    $listedate=array();
    $listedate[]=date("01-01-".$annee);
    $listedate[]=date("01-05-".$annee);
    $listedate[]=date("08-05-".$annee);
    $listedate[]=date("14-07-".$annee);
    $listedate[]=date("15-08-".$annee);
    $listedate[]=date("01-11-".$annee);
    $listedate[]=date("11-11-".$annee);
    $listedate[]=date("25-12-".$annee);
    $datepaques=strtotime(paques($annee));

    $listedate[]=date('d-m-Y',strtotime('+1 day',$datepaques));
    $listedate[]=date('d-m-Y',strtotime('+39 days',$datepaques));
    $listedate[]=date('d-m-Y',strtotime('+50 days',$datepaques));
    return $listedate;
}

// cette fonction permet de trouver le jour ouvré correspondant a une date + ou - un nombre de jour
function trouvejourouvre($dateactuelle,$decalage)
{
    $datecalculee=strtotime($dateactuelle);
    $datecalculee+=($decalage*86400);
    // a ce stade, $datecalcule contient la date demandée sans tenir compte des jours ouvrés.

    //le décalage ensuite se fera jour par jour en plus ou en moins selon le décalage initiale
    $decalage=($decalage>0)?86400:-86400;
    //boucle
    $x=0;
    do {
        $x++;
        // Si le jour suivant n'est ni un dimanche (0) ou un samedi (6), ni un jour férié, on sort, sinon on ajoute ou on retire un jour
        if (!in_array(date('w', $datecalculee), array(0, 6)) && !in_array(date('d-m-Y',$datecalculee), ferie(date("Y",$datecalculee)))) {
            break;

        } else {
            $datecalculee+=$decalage;
        }
    }  while ($x<10); // petite sécurité,certes inutile mais je déteste les boucles infinies
    return( date('d-m-Y',$datecalculee));

}

?>

#!/usr/bin/php^M : mauvais interpreteur de commande

Lorsque que l’on écrit un script php destiné a être une ligne de commande linux, il faut s’assurer le texte a ses retours de chariot formaté façon linux (LF). Si ce n’est pas le cas on obtient ce message d’erreur.

Pour le changer dans phpstorm il faut cliqué en bas a droite de la fenêtre de phpstorm sur

CR/LF pour le changer en LF

 

Dans ultraedit c’est dans le menu édition=>Convertir les sauts de ligne => Convertir en Format UNIX (LF)

 

Après réenregistrement le script s’exécutera normalement en ligne de commande.

script php lancé sur création de fichier avec incron sur ubuntu 14.04

Objectif:
Créer un script php exécutant un traitement sur un fichier reçu dans un dossier. Dans mon cas, c’était pour l’intégration automatique de fichiers csv reçu par ftp dans une base de donnée.

Méthode utilisée: Incron

Incron:
Logiciel qui permet de planifier le lancement d’un programme ou d’un script sur un événement système (fichier créé ou modifié etc…)
Très utilise pour réaliser un process se déclenchant à la réception d’un fichier.
L’installation de Incron se fait apt-get install.
La seule chose déroutante c’est que par défaut l’utilisateur root lui-même est interdit et donc il faut l’autoriser.

Donc cela donne:

sudo apt-get install incron
sudo vi /etc/incron.allow

=> ajouter une ligne contenant juste
root
=> enregistrer & quitter

sudo incrontab -e

=> on arrive sur nano et l’on peu ajouter

/var/www/sentinelle/depot IN_CLOSE_WRITE /var/www/sentinelle/sentinelle.php "$#"

=> lors de la création d’un fichier dans /var/www/sentinelle/depot,  le script sentinelle.php sera lancé avec le nom du fichier en argument.
La commande:

sudo chmod +x /var/www/sentinelle/sentinelle.php

aura été préalablement lancé pour rendre le script « exécutable » depuis un shell.

exemple de contenu du script schellscript.php:

#!/usr/bin/php
<?php
// $arg[0] correspond au nom du script
// $arg[1] correspond au 1er parametre
// $arg[2] correspond au 2eme parametre etc...

// si le nombre de parametres est différent du nombre prévu ou que le 1er correspond 
// a une demande d'aide on affiche l'aide 
if ($argc != 2 || in_array($argv[1], array('--help', '-help', '-h', '-?'))) {
?>

Ce script traite le fichier reçu en argument.

  Utilisation :
  <?php echo $argv[0]; ?> <option>

  <option> doit être le nom de fichier.
  Avec les options --help, -help, -h,
  et -?, vous obtiendrez cette aide.

<?php
// sinon execute le traitement normal du script
} else {
    echo $argv[1]."\n";
}
?>

Note: Attention à ce que le texte du script soit au format linux (retour à la ligne par \n) car ce dernier n’aime pas les scripts comportant des retour à la ligne format Microsoft (\r\n).

reférences:

Un excellent tutorial d’INCRON: http://www.admin-linux.fr/?p=4840
Un autre: http://www.cyberciti.biz/faq/linux-inotify-examples-to-replicate-directories/
Un dernier: http://www.howtoforge.com/triggering-commands-on-file-or-directory-changes-with-incron