Archives de
Category: Code

Docker et PHP

Docker et PHP

Après un petit bout de temps à maintenir et mettre a jours php, apache et mysql sur mon par diverse moyen j’ai décidé de passer a docker. Il m’a fallut plusieurs essaie et tâtonnement pour arriver à une configuration qui me convient pour un environnement de développement.

C’est disponible sur ce dépôt github. Il y a deux conteneurs.

Le premier et le conteneur mysql de base avec un seul utilisateur root et comme mot de passe root.

Le second est un conteneur php avec apache plus quelques extensions ainsi que composer accessible par la commande composer :

  • apcu
  • xdebuh
  • intl
  • opcache
  • gd
  • mysqli
  • pdo
  • zip

Apache est configuré avec ssl et rewrite.

Pour utiliser xdebug, mettez à jour le fichier php.ini avec votre IP pour xdebug.remote_host=192.168.1.10 puis dans l’url ajoutez « ?XDEBUG_SESSION_START= »

 

API Platform + FOSUserBundle + LexikJWTAuthenticationBundle

API Platform + FOSUserBundle + LexikJWTAuthenticationBundle

  • Symfony: 3.2.2
  • API Platform: 2.0.3
  • FOSUserBundle: 2.0.0-beta2
  • LexikJWTAuthenticationBundle: 2.1.1

En premier lieu nous allons cloner le dépôt API Plaftorm:

composer create-project api-platform/api-platform bookshop-api

Une fois fait, nous installons FOSUserBundle et LexikJWTAuthenticationBundle via composer:

composer require friendsofsymfony/user-bundle:2.0.0-beta-2
composer require lexik/jwt-authentication-bundle

Puis nous activons les bundles fraichement installé dans AppKernel.php:

public function registerBundles()
{
    return array(
        // ...
        new FOS\UserBundle\FOSUserBundle(),
        new Lexik\Bundle\JWTAuthenticationBundle\LexikJWTAuthenticationBundle(),
    );
}

Nous créons notre entité User.php:

<?php

namespace AppBundle\Entity;

use ApiPlatform\Core\Annotation\ApiResource;
use Doctrine\ORM\Mapping as ORM;
use FOS\UserBundle\Model\User as BaseUser;
use FOS\UserBundle\Model\UserInterface;
use Symfony\Component\Serializer\Annotation\Groups;

/**
 * @ORM\Entity
 * @ApiResource(attributes={
 *     "normalization_context"={"groups"={"user", "user-read"}},
 *     "denormalization_context"={"groups"={"user", "user-write"}}
 * })
 */
class User extends BaseUser
{
    /**
     * @ORM\Id
     * @ORM\Column(type="integer")
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    protected $id;

    /**
     * @Groups({"user"})
     */
    protected $email;

    /**
     * @ORM\Column(type="string", length=255, nullable=true)
     * @Groups({"user"})
     */
    protected $fullname;

    /**
     * @Groups({"user-write"})
     */
    protected $plainPassword;

    /**
     * @Groups({"user"})
     */
    protected $username;

    public function setFullname($fullname)
    {
        $this->fullname = $fullname;

        return $this;
    }
    public function getFullname()
    {
        return $this->fullname;
    }

    public function isUser(UserInterface $user = null)
    {
        return $user instanceof self && $user->id === $this->id;
    }
}

Nous poursuivons par la génération des clés, notez bien la pass phrase qu’il faudra copié dans le fichier parameters.yml

mkdir -p var/jwt # For Symfony3+, no need of the -p option
openssl genrsa -out var/jwt/private.pem -aes256 4096
openssl rsa -pubout -in var/jwt/private.pem -out var/jwt/public.pem

Nous mettons a jours les fichier suivants:

config.yml:

framework:
    translator:      { fallbacks: ["%locale%"] }

fos_user:
    db_driver: orm # other valid values are 'mongodb', 'couchdb' and 'propel'
    firewall_name: main
    user_class: AppBundle\Entity\User

lexik_jwt_authentication:
    private_key_path: '%jwt_private_key_path%'
    public_key_path:  '%jwt_public_key_path%'
    pass_phrase:      '%jwt_key_pass_phrase%'
    token_ttl:        '%jwt_token_ttl%'

routing.yml

api:
    resource: '.'
    type:     'api_platform'

app:
    resource: '@AppBundle/Action/'
    type:     'annotation'

api_login_check:
    path: /api/login_check

fos_user_security:
    resource: "@FOSUserBundle/Resources/config/routing/security.xml"

fos_user_profile:
    resource: "@FOSUserBundle/Resources/config/routing/profile.xml"
    prefix: /profile

fos_user_register:
    resource: "@FOSUserBundle/Resources/config/routing/registration.xml"
    prefix: /register

fos_user_resetting:
    resource: "@FOSUserBundle/Resources/config/routing/resetting.xml"
    prefix: /resetting

fos_user_change_password:
    resource: "@FOSUserBundle/Resources/config/routing/change_password.xml"
    prefix: /profile

security.yml

security:
    encoders:
        FOS\UserBundle\Model\UserInterface: bcrypt

    role_hierarchy:
        ROLE_READER: ROLE_USER
        ROLE_ADMIN: ROLE_READER

    providers:
        fos_userbundle:
            id: fos_user.user_provider.username_email

    firewalls:
        login:
            pattern:  ^/login
            stateless: true
            anonymous: true
            provider: fos_userbundle
            form_login:
                check_path: /login_check
                username_parameter: _username
                password_parameter: _password
                success_handler: lexik_jwt_authentication.handler.authentication_success
                failure_handler: lexik_jwt_authentication.handler.authentication_failure
                require_previous_session: false

        main:
            pattern: ^/
            provider: fos_userbundle
            stateless: true
            anonymous: true
            lexik_jwt: ~

        # disables authentication for assets and the profiler, adapt it according to your needs
        dev:
            pattern: ^/(_(profiler|wdt)|css|images|js)/
            security: false

    access_control:
        - { path: ^/login, role: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/books, roles: [ ROLE_READER ] }
        - { path: ^/, roles: [ ROLE_READER ] }

parameters.yml.dist et parameters.yml, n’oubliez pas la pass phrase pour les clés

parameters:
    # ...
    jwt_private_key_path: '%kernel.root_dir%/../var/jwt/private.pem' # ssh private key path
    jwt_public_key_path:  '%kernel.root_dir%/../var/jwt/public.pem'  # ssh public key path
    jwt_key_pass_phrase:  ''                                         # ssh key pass phrase
    jwt_token_ttl:        3600

Puis nous mettons à jour la base de données et créons notre premier utilisateur:

php bin/console doctrine:schema:update --force
php bin/console fos:user:create testuser test@example.com p@ssword
php bin/console fos:user:promote testuser ROLE_READER

Pour tester ca un petit coup de Curl:

curl -X POST http://127.0.0.1:8000/login_check -d _username=test@example.com -d _password=p@ssword

Et comme réponse nous avons:

{"token":"eyJhbGciOiJSUzI1NiJ9.eyJyb2xlcyI6WyJST0xFX1JFQURFUiIsIlJPTEVfVVNFUiJdLCJ1c2VybmFtZSI6InRlc3R1c2VyIiwiZXhwIjoxNDg2NDk5MjczLCJpYXQiOjE0ODY0OTU2NzN9.RL8VS8MuKhvg0uW23TPnlxsZusUIE86YuE_cg44F4rToe8c7zV6s-2lmuOhP5CyeB50IVfLlb4-RC1frKQFTIrEA1LJ2FewxKFO2Mb2sIQwQonAew3MTwnxbnPRWrS0sQCsb1NbXTxohc5WH-BU1U3IkuAjuTVLcB3g7JlPor0yedUU766BRXjIhklN7IuILKsjhteqGK6F9Ilg0zmLyjQV3-BUg1nnWygQ-O4BLrU_InvhpHZVOnPfikMftcKVAhn_Z4GKGVVORQxplhc4i0lJgzdV83AaImYqOzn3WBJxKnzmitb6rnOJ-SKXjdAYRix_rFwqIzzWNdcAg1-C731b08I3qcfWXmmadI2GsygVpemlagH8v6tcFjwVsyazblqFCA8oy37N0CfSVt7av8GIbenWtIDUS5Tl3a0SMxmcwg6rlTL_1pE0-E8mPa8ZbLho-nON2hKoQcG1LhIeuatzIi7lUSLCQv_uzy29Hpq7PPJ_43cZZDdex-vk3BVHbjZCOcggVxp-mDDTcU7VxZFwmi8AiP9ZAg8AFXviKcfM7D5Er2tB2k3HHsnCqTSGRJe9czNs-GaW55748BK_u0UiEBvBdDHUgmbGw4UTRw8aNTY8p8M_tavezC36p5zilyd2CB6knsLtm0aRkqyZaiK27uvajYMt65BpxqsmXzLw"}

Et voila !!!

Disponible sur github.

Rails 4 et MongoDB part 4 : Heroku

Rails 4 et MongoDB part 4 : Heroku

Heroku est un service paas pour héberger des applications facilement. Afin de s’en servir facilement il faut suivre quelques conseils.

Premièrement, modifiez votre fichier mongoid.yml pour y ajouter :

production:
  sessions:
    default:
      uri: <%= ENV['MONGOHQ_URL'] %>
      options:
        skip_version_check: true
        safe: true

Une fois fait, ajoutez la gem rails_12factor à votre projet.

Créez-vous ensuite un compte sur Heroku, puis installez la Heroku Toolbelt. Allez alors dans le dossier de votre application et lancez les commandes suivantes :

heroku login
heroku create --region eu
heroku addons:add mongohq:small
git push heroku master

La 3e ligne sert à ajouter une base MongoDB à votre instance Heroku.

Une fois fait vous devriez alors pouvoir accéder à votre application en ligne.

Si vous utilisez Travi-CI vous pouvez le configurer afin qu’après un build réussît, le déploiement se fasse sur Heroku. Pour ce faire installer la gem travis avec la commande suivante :

gem install travis -v 1.5.4 --no-rdoc --no-ri

Toujours dans le dossier de votre application lancez la commande suivante pour mettre à jour votre fichier .travis.yml avec les informations requises pour Heroku :

travis setup heroku

Rajoutez la ligne run: rake assets:precompile dans la section deploy de votre fichier .travis.yml.

Lors de votre prochain push sur GitHub, à la fin du build Travis si vos tests sont tous passés, votre instance d’Heroku sera mise à jour.

Rails 4 et MongoDB part 3 : Travis

Rails 4 et MongoDB part 3 : Travis

Tout d’abord, qu’est-ce que Travis-CI ?

Travis-CI est un serveur d’intégration continue gratuit pour les projects open source.

Il est utilisé par beaucoup de projet afin de vérifier que les pull request n’introduise pas de bug et qu’il n’y a pas de régression lors de modifications.

Afin de l’utiliser il faut suivre les étapes suivantes:

  • Se connecter sur travis-ci.org avec son compte GitHub
  • Activer les dépôts que l’on souhaites tester
  • Ajouter un fichier .travis.yml a la racine de notre projet

Le fichier .travis.yml contient les informations dont Travis-CI a besoin afin de lancer les tests de votre dépôt.

Dans le cas de notre projet RoR avec MongoDB, il nous faut spécifier :

  • le langague
  • la version de interpréteur Ruby
  • notre base de données
  • l’environnement et la commende pour lancer les tests

Au final notre fichier .travis.yml sera le suivant :

language: ruby
rvm:
  -  2.0.0

services:
  - mongodb

script:
  - RAILS_ENV=test
  - bundle exec rake cucumber

Une fois se fichier disponible sur GitHub, Travis-CI va automatiquement lancer les tests a chaque commit.

Pour le dépôt du projet GitHub utilisé pour mes articles, la page Travis-CI est la suivante : https://travis-ci.org/dayofr/rails_tuto

On peux y voir le dernier test, la sortie de la console ainsi que l’historique des builds :

travis

Il est possible de rajouter un badge fin d’indiquer l’état actuel des tests du projet, généralement dans le fichier README à la racine du dépôt.

Rails 4 et MongoDB part 2 : Cucumber

Rails 4 et MongoDB part 2 : Cucumber

Dans l’article précédent, nous nous étions arrêté après l’utilisation du scaffolding pour créer un document dans MongoDB et le CRUD qui va avec.

Parmi les fichiers générés nous avons :

Afin de commencer par de bonnes pratiques, nous allons créer nos premiers tests BDD avec Cucumber.

Le BDD, Behavior Driven Developmen, est une méthode agile qui consiste à écrire des tests de scénarios décrivant le comportement de l’application dans un langage le plus proche possible de celui de l’humain. Un exemple valant mieux qu’un long discoure, voilà le scénario décrivant la page de listing des posts :

Scenario: Posts List
    Given I have posts titled Paris, Marseille
    When I go to the list of posts
    Then I should see "Paris"
    And I should see "Marseille"

En lisant ce texte, il est facile de savoir ce qu’il se passe et ce qui va être testé.

Afin de commencer, copiez le code suivant dans le fichier features/posts.feature :

Feature: Manage posts
  In order to make a blog
  As an author
  I want to create and manage posts

  Scenario: Posts List
    Given I have posts titled Paris, Marseille
    When I go to the list of posts
    Then I should see "Paris"
    And I should see "Marseille"

Ici nous avons juste rajouté le bloc Feature afin de décrire la fonctionnalité qui sera testée.
Une fois fait, en ligne de commande lancez : rake cucumber

Vous devriez alors avoir la sortie suivante :

cucumber1Cucumber nous indique ici qu’il ne comprend pas les différentes étapes de notre test. En effet il ne peut pas comprend sans qu’on lui explique ce qu’il faire pour la ligne Given I have posts titled Paris, Marseille. Il nous faut écrire le code correspondant a cette étape. Créez alors un fichier step_definitions/posts_steps.rb puis ajoutez-y :

Given /^I have posts titled (.+)$/ do |names|
  names.split(', ').each do |name|
    Post.create!(:name => name)
  end
end

Que faisons-nous ici ? La 1re ligne est une expression régulière afin de définir quelle étape nous définissons, elle capture tout ce qui suit et le fournit dans la variable names .

À la ligne 2, nous divisons la variable sur et itérons sur les valeurs retournées afin de les ajouter dans MongoDB grâce à la ligne 3.

Si nous relançons les tests nous pouvons voir que nous somme allez plus loin cette fois.

cucumber2Il ne nous reste plus qu’à définir les autres étapes de notre test afin d’avoir 100% dans nos scénarios :

cucumber3Pour voir le code, j’ai créé un dépôt GitHub : https://github.com/dayofr/rails_tuto .

Dans le prochain article je parlerais d’intégration continue avec Travis-ci.