PHP Derrière un Proxy

Note : Dans cet article quand je parle de proxy, c’est un serveur filtrant les entrées/sorties d’un réseau sur le web. Pour qu’une machine (serveur ou poste client) puisse accéder au web et surfer, il faut que le proxy soit correctement configuré pour que toutes les connexions réseaux passent par lui.

Ne nous le cachons pas, PHP hébergé derrière un proxy c’est juste la merde. Il n’est pas possible d’effectuer une configuration de manière unique pour que toutes les applications PHP passe par un proxy. Il faut donc prendre en compte dans son développement cette problématique.

Quelques exemple pour éclairer mes propos :
SoapClient, SoapServer, CURL prennent les configurations de connexion au proxy en paramètres.
Les fonctions de la famille file_get_contents, peuvent prendre en paramètres les configurations proxy, ou être configurées de manière globale grâce au système de stream context.
Et il y a les fonctions qui ne se configurent pas du tout comme exif_imagetype.

La question qui se pose, comment se faciliter la vie et arrêter de se prendre la tête avec ce p****n de PROXY de M***E ?

On inspire profondément, puis on expire.

Si vous utilisez Symfony2, il ne vous reste plus qu’a installer le superbement magnifiquement génialissime BehindAProxyBundle (Oui il est de moi ;) ) et lire la doc.

Dans le cas contraire vous pouvez utiliser le code du ProxyService.php.

Que fait la classe ProxyService ?

Elle centralise les options de configurations de connexion au proxy de différentes fonctions.
On y retrouve ce qu’il faut pour des connexions SOAP, CURL, ou stream context.

Qu’en est-il pour les fonctions qui ne peuvent pas être configuré comme exif_imagetype ?

La solution palliative à ce problème est de charger le fichier distant sur le serveur dans un fichier temporaire. (Genre le mec, il se la pète avec une phrase super longue qui ne veux rien dire…)

Le plus simple pour comprendre c’est de coder tout ça.

Dans notre cas nous coderons en dure le stream context sans passer par des classes externes.

function loadFileTemporarily($url) {

// Notre stream Context
$context = array(
                'http' => array(
                    'proxy' => 'tcp://172.0.0.1:8080',
                    'request_fulluri' => true,
                )
            );
$cxContext = stream_context_create($context);

$tmpfile = file_get_contents($file, false , $cxContext);

               // Ecriture du contenu récupéré dans un fichier temporaire
            $tempStreamFile = tmpfile();
            fwrite($tempStreamFile, $tmpfile);
            fseek($tempStreamFile, 0);

    // Récupération d’information par rapport à ce fichier temporaire
    $streamMetadData = stream_get_meta_data($tempStreamFile);

    // On retourne l’uri
            return $streamMetadData['uri'];
}

echo "<pre>";
print_r(
exif_imagetype(
loadFileTemporarily( "http://perdu.com//une-image.jpg" )
)
);
echo "</pre>";

J’utilise cette méthode dans un fork de ImageBundle de Gregwar.

Comment configurer le stream contex de manière globale ?

Pour les fonctions PHP qui utilisent le système de stream context, il est possible de définir les paramètres de manière globale.

Pour cela on utilise la fonction stream_context_set_default. Cette fonction doit être placé dans un fichier de configuration (au même niveau que la connexion à une base de données).

Ci-dessous un exemple d’implémentation.

// Dans un fichier de configuration

$context = array(
                'http' => array(
                    'proxy' => 'tcp://127.0.0.1:8080',
                    'request_fulluri' => true,
                )
            );

stream_context_set_default($context);
Publié dans PHP, Symfony2

KnpMenuBundle Corrspondance avec le menu en cours

Le problème que concerne cet article est encore lié au KnpMenuBundle et fait suite à un précédent article sur le KnpMenuBundle et les fils d’Ariane.

Le problème que nous sommes nombreux à rencontrer, est que ce bundle ne fait pas la correspondance de route avec paramètre(s).

une route en Symfony2 peut être associé à un nom.
Par exemple si l’on utilise les annotations dans les controller, on peux avoir quelque chose comme ça :

class amazingController extends Controller {
/**
 * @Route("/a-funky-route" name="_a_funky_route")
 */
public function getHeadachesAction()
{
    //...
}

/**
 * @Route("/say-my-name/{myName}" name="_say_my_name", defaults={"myName"="Arthur Dent"})
 */
public function sayMyNameAction($myName)
{
    echo $myName; // <- ça c'est pas bien, c'est juste pour l'exemple ;)
}

Et si l’on configure notre Menu Builder comme ce-ci :

class Builder extends ContainerAware {
//...
 public function menu(FactoryInterface $factory, array $options) {

        $menu = $factory->createItem('menu');

            $menu->addChild('Menu 1', array('route' => '_a_funky_route'));
            $menu->addChild('Menu 2', array('route' => '_say_my_name', 'routeParameters' => array('myName' => null)));

        return $menu;
    }
}

Dans ce cas de configuration, KnpMenuBundle est incapable de faire correspondre une url http://site.fr/say-my-name/Pedro avec la route _say_my_name.

C’est là où l’on va ajouter un peu de magie dans tout ça !

En premier lieu, le code de @merk que l’on va un peux améliorer.

namespace WaldoMyFunTaStikBundleVoter;

use KnpMenuItemInterface;
use KnpMenuMatcherVoterVoterInterface;
use SymfonyComponentDependencyInjectionContainerInterface;

class RequestVoter implements VoterInterface {
   /**
     * @var SymfonyComponentDependencyInjectionContainerInterface
     */
    private $container;

    public function __construct(ContainerInterface $container)
    {
        $this->container = $container;
    }

    /**
     * Checks whether an item is current.
     *
     * If the voter is not able to determine a result,
     * it should return null to let other voters do the job.
     *
     * @param ItemInterface $item
     * @return boolean|null
     */
    public function matchItem(ItemInterface $item)
    {

        /* @var $request SymfonyComponentHttpFoundationRequest */
        $request = $this->container->get('request');

        if ($item->getUri() === $request->getRequestUri()) {
            return true;
        }

        // C'est ici que l'on vérifie que la route en cours d'utilisation est bien la même
        // que celle contenu dans l'item que nous passe le KnpMenuBundle
        if ($item->getExtra('routes') !== null && in_array($request->attributes->get('_route'), $item->getExtra('routes'))) {
            return true;
        }

        return null;
    }
}

Ce bout de code Xml est à placer dans config/service.xml

<service id="waldo.voter.request" class="WaldoMyFunTaStikBundleVoterRequestVoter">
  <tag name="knp_menu.voter" />
  <argument type="service" id="service_container" />
</service>

Besoin de comprendre ?

$item->getExtra('routes') contient un tableau composé de route(s) définis dans le builder de menu : $menu->addChild('Menu 1', array('route' => '_a_funky_route'));.
Et $request->attributes->get('_route') donne le nom de la route qui correspond à l’url de la page que l’utilisateur a appelé.
On ne se base plus sur les URLs générés, comme le fait le KnpMenuBundle par défaut, mais sur le nom des routes qui ne change jamais, paramètres ou non.

Tagués avec :
Publié dans Symfony2, Symfony2 v2.1

KnpMenuBundle Comment faire un Breadcrumb

Cet article est obsolète, il existe une façon plus simple de faire un fil d’Ariane avec le Knp Menu Bundle.

Pas évident de trouver comment faire un fil d’Ariane ( ou breadcrumb ) avec le KnpMenuBundle

On va la faire courte.
Dans un premier temps on va coder dans le « Builder » de menu, puis on va faire un peu de Twig

Le Builder

namespace WaldoColorfullBundleMenu;

use KnpMenuFactoryInterface;
use SymfonyComponentDependencyInjectionContainerAware;

class Builder extends ContainerAware
{
    public function mainMenu(FactoryInterface $factory, array $options)
    {
        $menu = $factory->createItem('main');

        $menu->addChild('Entrée 1', array('route' => '_route_entre_1'))
                ->addChild('Entrée 1.1', array('route' => '_route_entre_1_1'))
                ->addChild('Entrée 1.1.1', array('route' => '_route_entre_1_1_1'));

        $menu->addChild('Entrée 2', array('route' => '_route_entre_1'))
                ->addChild('Entrée 2.1', array('route' => '_route_entre_2_1'))
                ->addChild('Entrée 2.1.1', array('route' => '_route_entre_2_1_1'));

        return $menu;
    }

    // Code pompé honteusement ici https://github.com/KnpLabs/KnpMenuBundle/issues/122#issuecomment-12149706
    public function myBreadCrumb(FactoryInterface $factory, array $options)
    {
        $menu = $this->leftExtranetMenu($factory, $options);

        $matcher = $this->container->get('knp_menu.matcher');
        $voter = $this->container->get('knp_menu.voter.router');
        $matcher->addVoter($voter);

        $treeIterator = new RecursiveIteratorIterator(
                        new KnpMenuIteratorRecursiveItemIterator(
                                new ArrayIterator(array($menu))
                        ),
                        RecursiveIteratorIterator::SELF_FIRST
        );

        $iterator = new KnpMenuIteratorCurrentItemFilterIterator($treeIterator, $matcher);

        // Set Current as an empty Item in order to avoid exceptions on knp_menu_get
        $current = new KnpMenuMenuItem('', $factory);

        foreach ($iterator as $item) {
            $item->setCurrent(true);
            $current = $item;
            break;
        }

        return $current;
    }

}

Le Twig

{% set currentItem = knp_menu_get('WaldoColorfullBundle:Builder:myBreadCrumb').getBreadcrumbsArray %}

{% for item in currentItem %}
    {% if loop.index != 1 %}
        {% if loop.index > 2 %} > {% endif %}
        {% if not loop.last %}<a href="{{ item.uri }}">{{ item.label }}</a>
        {% else %}<span>{{ item.label }}</span>{% endif %}
    {% endif %}
{% endfor %}
Tagués avec : , , ,
Publié dans Symfony2, Symfony2 v2.1

Symfony2 – Envoie d’emails par la ligne de commande

Logo du Framework Symfony2Lorsque l’on essaye d’envoyer un email en utilisant la ligne de commande (cmd) offerte par Symfony2, on se heurte à un petit problème.

Dans le fichier de configuration « config.yml » est défini pour swiftmailer ce paramètre : spool: { type: memory }

Cela signifie que tout les mails envoyé sont mis dans un spool mémoire. Dans le cas d’un envoie depuis un controller, l’email est placé dans le spool et envoyé une fois que le controller à terminé (par le biais des event), cela évite que la page mette trop de temps à ce charger.

Dans le cas d’un déclenchement par la cmd, il n’y a pas d’événement qui déclenche l’envoie d’email. Il faut donc le faire à la main !

namespace waldoUnBundleCommand;

use SymfonyBundleFrameworkBundleCommandContainerAwareCommand;
use SymfonyComponentConsoleInputInputInterface;
use SymfonyComponentConsoleOutputOutputInterface;

class CronCommand extends ContainerAwareCommand
{
    protected function configure()
    {
        $this
                ->setname('uranie:cron')
                ->setDescription('Exécute les crons');
    }

    public function execute(InputInterface $input, OutputInterface $output)
    {
        $message = Swift_Message::newInstance()
            ->setSubject('Coucou Email')
            ->setFrom('send@example.com')
            ->setTo('recipient@example.com')
            ->setBody("un super message");
        $this->getContainer()->get('mailer')->send($message);

	 // Du fait qu'il y est un spool de mail en mémoire définit dans le fichier de config et que ce spool est uber pratique dans le cadre des controller
        // On force l'envoie des mails du spool avec le code-ci dessous
        /* @var $mailer Swift_Mailer */
        $mailer = $this->getContainer()->get('mailer');

        $transport = $mailer->getTransport();
        if ($transport instanceof Swift_Transport_SpoolTransport) {
            $spool = $transport->getSpool();
            $sent = $spool->flushQueue($this->getContainer()->get('swiftmailer.transport.real'));
        }
    }

Rien de bien compliqué en somme.

Publié dans PHP, Symfony2, Symfony2 v2.1

Doctrine2 – Truncate Table dans DataFixture

Un vrais Truncate Table avec doctrine dans les DataFixture

Doctrine étant parfois un peu quiche… Lorsqu’on lui demande de faire un truncate table lors de l’exécution de la commande de datafixture, doctrine est tout simplement incapable de réaliser cette action car il existe des relations entre les tables.

Donc voici une méthode à utiliser dans vos dataFixtures qui permet de faire un trucate table qui fonctionne (dans mon cas en tout cas…).
Les deux seules choses à paramétrer sont l’emplacement de vos Entity et le début de leur namespace.


namespace WaldoWonderlandBundleDataFixturesORM;

use DoctrineCommonPersistenceObjectManager;
use DoctrineCommonDataFixturesFixtureInterface;
use DoctrineCommonDataFixturesOrderedFixtureInterface;
use SymfonyComponentDependencyInjectionContainerAware;
use SymfonyComponentFinderFinder;

/**
 * Purge de la base de données
 * Purge/Vide les données de la base de données
 * La base étant géré par le moteur InnoDB qui prend en compte les relations entre les table, Doctrine est dans l'incapacité à effectuer cette tâche
 *
 *
 * @uses $ php app/console doctrine:fixtures:load --append
 */
class Load00TruncateTable extends ContainerAware implements FixtureInterface, OrderedFixtureInterface
{

    private $tableToTruncateList = array();
    private $connection;

    //A voir pour être utilisé dans le CronBundle
    public function load(ObjectManager $manager)
    {

        $this->connection = $manager->getConnection();

        $this->connection->executeUpdate("SET FOREIGN_KEY_CHECKS=0;");

        /* @var $classMetadata DoctrineORMMappingClassMetadata */
        foreach ($manager->getMetadataFactory()->getAllMetadata() as $classMetadata) {

            $this->truncateTable($classMetadata->getTableName());

            foreach ($classMetadata->getAssociationMappings() as $field) {
                if (isset($field['joinTable']) && isset($field['joinTable']['name'])) {
                    $this->truncateTable($field['joinTable']['name']);
                }
            }
        }

        $this->connection->executeUpdate("SET FOREIGN_KEY_CHECKS=1;");
    }

    private function truncateTable($tableName)
    {
        if (!in_array($tableName, $this->tableToTruncateList)) {
            $this->connection->executeUpdate($this->connection->getDatabasePlatform()->getTruncateTableSQL($tableName));
            $this->tableToTruncateList[] = $tableName;
        }
    }

    /**
     * Définit dans quel ordre les fixtures doivent être lancé
     */
    public function getOrder()
    {
        return 1; // the order in which fixtures will be loaded
    }

}

C’est classe, non?

Publié dans Doctrine, PHP, Symfony2

Symfony2 – Rafraîchir un token utilisateur depuis un controller

Voici une méthode qui permet de rafraîchir un token utilisateur.Logo du Framework Symfony2
Rappel, on accède dans un contrôleur au token grâce à la méthode :

$this->get('security.context')->getToken()->getUser()

Je suis pas sûr que ce soit la meilleur façon de procéder, mais dans l’urgence ça a le mérite de fonctionner…

use SymfonyComponentHttpKernelEventGetResponseEvent;
use SymfonyComponentHttpKernelHttpKernelInterface;

[…]

$contextListnerClass = $this->container->getParameter("security.context_listener.class");
$t = $this->get('security.firewall.map.context.secured_area')->getContext();
foreach ($t[0] as $v) {
    if ($v instanceof $contextListnerClass) {
        $v->handle(new GetResponseEvent($this->get('kernel'), $this->getRequest(), HttpKernelInterface::MASTER_REQUEST));
    }
}

Je vous ais livré le code sans explication, car je ne sais pas vraiment quoi dire…
Donc à utiliser avec précaution.

Publié dans PHP, Symfony2, Symfony2 v2.1

Relation ManyToMany des entités Doctrine

Logo DoctrineSuite du tutoriel sur la création des relations entre entités pour leur persistance en base de données avec Doctrine.

Pour les précédent tutoriels :

Dans cette partie nous traiterons de la relation ManyToMany.

relation ManyToMany Lire la suite ›

Publié dans Doctrine, PHP, Symfony2

Relation OneToMany et ManyToOne des entités Doctrine

Logo DoctrineSuite du tutoriel sur la création des relations entre entités pour leur persistance en base de données avec Doctrine.

Pour les précédent tutoriels :

Dans cette partie nous traiterons de OneToMany et sa réciproque ManyToOne

Shchéma merise d'une relation OneToMany ManyToOneUne région possède zéro ou plusieurs départements.
Un département appartient à une et une seul région. Lire la suite ›

Publié dans Doctrine, PHP, Symfony2

Lire un fichier Microsoft Excel (.xls) avec PHP

Logo de la librairie PHPExcelCe tutoriel ce veut simplisme. On va juste voir comment lire un fichier Excel afin de le représenter au format HTML.

Pour cela on va utiliser la librairie PHPExcel de CodePlex.
Contrairement au tutoriel précédent, cette librairie est maintenue et ne pose pas de problème d’encodage.
La librairie est bien documentée et un grand nombre d’exemples sont fournis et elle est utilisée dans PhpMyAdmin.

require_once '.Classes/PHPExcel/IOFactory.php';

// Chargement du fichier Excel
$objPHPExcel = PHPExcel_IOFactory::load("unFichierExcel.xls");

/**
* récupération de la première feuille du fichier Excel
* @var PHPExcel_Worksheet $sheet
*/
$sheet = $objPHPExcel->getSheet(0);

echo '<table border="1">';

// On boucle sur les lignes
foreach($sheet->getRowIterator() as $row) {

   echo '<tr>';

   // On boucle sur les cellule de la ligne
   foreach ($row->getCellIterator() as $cell) {
      echo '<td>';
      print_r($cell->getValue());
      echo '</td>';
   }

   echo '</tr>';
}
echo '</table>';

Voila. C’est tout. Rien de compliqué.

Par contre si vous voulez aller plus loin, la librairie regorge de fonctionnalités pour la lecture et l’écriture de fichier. Et elle ne se contente pas du format xls elle prend aussi en charge le xlsx d’Excel 2007.

Tagués avec : , ,
Publié dans PHP

Mappage des entités avec les annotations de Doctrine.

Logo DoctrinePetit tutoriel sur le mappage des entités (que l’on appel Entity) avec doctrine et son système d’annotation.

On ne verra que quatre types de relation (je ne sais même pas s’il y en existe d’autre) :

Et pour que le choses soit un peu plus graphique et compréhensible, je vous met un exemple pour chaque avec le code SQL des tables créés, les classes PHP en découlant et un schéma Merise basique.

Pourquoi Merise ? Alors qu’il faudrait lui préférer l’UML qui est quand même plus approprié pour les constructions de base de données objets. Simplement parce que les habitudes ont la vie dure et que l’on va encore manger du Merise pendant un grand nombre d’années. Lire la suite ›

Publié dans Doctrine, PHP, Symfony2