Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Symfony Local Web Server ... reloaded

Symfony Local Web Server ... reloaded

Fabien Potencier

December 06, 2018
Tweet

More Decks by Fabien Potencier

Other Decks in Technology

Transcript

  1. Back to the basics...
    part 2/n
    Fabien Potencier
    @fabpot

    View full-size slide

  2. 1) Email reloaded
    2) Local Web Server reloaded
    Fabien Potencier
    @fabpot

    View full-size slide

  3. The state of local web servers with Symfony
    php -S
    Many limitations
    Docker
    Might be slow/inconvenient
    Nginx/Apache
    Need setup
    Vagrant
    Not convenient

    View full-size slide

  4. My ideal setup?
    Local Web Server and local files

    Remote services

    Works well on the major platforms
    Fast and convenient

    View full-size slide

  5. PHP built-in server is almost perfect
    ... but it has too many limitations
    • Only one connection at a time

    • No HTTP-2 support

    • No SSL

    • Does not re-read env vars if they change

    • Needs some tweaks to make it work with Symfony (special router)

    • …

    View full-size slide

  6. A better local web server

    View full-size slide

  7. …via the new symfony binary
    • Introduced last week with

    • Binary with no dependencies, not even PHP

    No global Composer changes

    • Does more than just the Cloud

    • symfony security:check

    without calls to security.symfony.com
    • symfony new [--demo] [--full] ...

    to create new Symfony projects

    • and … https://symfony.com/cloud/download

    View full-size slide

  8. A Symfony local web server
    # From your project's directory
    $ symfony server:start
    # Done!
    First port available

    starts at 8000

    View full-size slide

  9. A Symfony local web server
    $ symfony server:start -d
    # open in the browser
    $ symfony open:local
    # tail the logs
    $ symfony server:log
    Start in the background

    View full-size slide

  10. Some features
    • No need to be root

    • Uses PHP-FPM whenever possible

    fallbacks to PHP-CGI/php -S (on Windows for instance)

    • HTTP-2 support out of the box

    • HTTP Link/pre-load/push support

    • Works with any PHP project

    Use it for plain HTML websites, Symfony ones, Laravel, Drupal, Magento, …

    • Fire and forget

    • Great developer experience…

    View full-size slide

  11. Managing multiple

    local versions of PHP

    View full-size slide

  12. Why?
    • Testing a new PHP version or a different configuration?

    • Different projects might need different PHP versions

    • Most machines have several PHP versions installed

    Think homebrew for instance

    View full-size slide

  13. Which PHP versions do you have locally?
    # Discover what you have
    $ symfony local:php:list

    View full-size slide

  14. Use any of them locally
    # Change the PHP version for the current directory
    $ echo "7.1" > .php-version
    # Or any 7.x version?
    $ echo "7" > .php-version
    # Done!

    View full-size slide

  15. Each web server can have its own PHP version
    # Detect the PHP version to use for this directory
    $ symfony server:start
    Algorithm
    (current directory and up)

    1/ .php-version?

    2/ .symfony.cloud.yaml?

    3/ $PATH

    View full-size slide

  16. The web server uses the “best” backend possible
    # Detect the best possible backend
    $ symfony server:start
    Algorithm
    1/ PHP-FPM

    2/ PHP-CLI (also FastCGI)

    3/ php -S

    View full-size slide

  17. Works also in your terminal
    # Use the symfony wrapper
    $ symfony php -v
    $ symfony php -r "echo 'Symfony!';"
    Algorithm
    (current directory and up)

    1/ .php-version?

    2/ .symfony.cloud.yaml?

    3/ $PATH

    View full-size slide

  18. Configure PHP per directory
    $ symfony php -r "echo ini_get('date.timezone');"
    Europe/Paris
    # Create a php.ini file at the project's directory
    $ cat php.ini
    [Date]
    date.timezone = Europe/Lisbon
    $ symfony php -r "echo ini_get('date.timezone');"
    Europe/Lisbon
    # Works with the web server as well

    View full-size slide

  19. # Using the wrapper all the time?
    # Create a copy named php
    $ cd ~/.symfony/bin/ && cp symfony php
    # Running php now runs the symfony wrapper!
    # Works with other PHP binaries
    $ cd ~/.symfony/bin/ && cp symfony pecl
    $ cd ~/.symfony/bin/ && cp symfony php-fpm
    $ cd ~/.symfony/bin/ && cp symfony php-config
    Want to drink the Kool-Aid?

    View full-size slide

  20. Logs, logs, logs

    View full-size slide

  21. All the logs
    $ symfony server:start -d
    $ symfony server:log
    Tail the logsss

    View full-size slide

  22. All the logs are parsed as semantic JSON logs and "humanized"
    From the
    web server
    From Symfony
    From PHP

    View full-size slide

  23. Errors are easier to spot

    View full-size slide

  24. # Use it even if you are using another web server
    # to get pretty Symfony logs
    $ symfony server:log --file=/path/to/var/log/dev.log

    View full-size slide

  25. Using TLS locally

    View full-size slide

  26. Why?
    • More similar to production

    • Detect mixed-content early

    • Having HTTPS is sometimes a requirement: stripe.js?

    View full-size slide

  27. How?
    # From anywhere
    $ symfony server:ca:install
    # Done!
    $ ls ~/.symfony/certs
    default.p12
    rootCA-key.pem
    rootCA.pem
    +Creates a local Certificate Authority

    +Registers it on your system trust store

    +Registers it on Firefox

    +Creates a default certificate

    for localhost/127.0.0.1/...

    View full-size slide

  28. https://127.0.0.1:8000/
    HTTP and HTTPS

    on the same port

    View full-size slide

  29. Local domain names

    View full-size slide

  30. Why?
    • Useful when a project behaviour depends on the domain
    name

    Think sub-domains like lisbon2018.symfony.com vs paris2019.symfony.com

    • No need to remember the ports

    ports can change, is it 8000 or 8001?

    • Stable endpoint across machines

    Think oauth2 redirect URL for instance

    • Use “regular” ports: 80 for HTTP and 443 for HTTPS

    • More similar to the production settings

    View full-size slide

  31. Optional, independent from the web server
    # From anywhere
    $ symfony proxy:start
    # Done ...almost!
    No need to be root

    View full-size slide

  32. + Nothing to install
    + No need to mess up with your DNS
    + Same configuration on all platforms
    + Does not intercept regular traffic

    View full-size slide

  33. How does it work?
    $ curl http://127.0.0.1:7080/proxy.pac
    // Only proxy *.wip requests
    // Configuration file in ~/.symfony/proxy.json
    function FindProxyForURL (url, host) {
    if (dnsDomainIs(host, '.wip')) {
    return 'PROXY 127.0.0.1:7080';
    }
    return 'DIRECT';
    }

    View full-size slide

  34. Optional, independent from the web server
    # Attach a domain to a directory
    $ symfony proxy:domain:attach symfony.com --dir=~/Code/symfony.com
    # Start the web server if needed
    $ symfony server:start -d
    # Done!
    https://symfony.com.wip/
    is now an “alias” for

    https://127.0.0.1:8xxx/

    View full-size slide

  35. Proxy Configuration
    {
    "tld": "wip",
    "port": 7080,
    "domains": {
    "*.symfony.com": ".../github/symfonycorp/symfony.com",
    "certif": ".../symfonycorp/certification.symfony.com",
    "certification": ".../symfonycorp/certification.symfony.com",
    "demo": "/tmp/demo",
    "cloud.symfony.com": ".../github/symfonycorp/symfony.com",
    "symfony.com": ".../github/symfonycorp/symfony.com"
    }
    }
    ~/.symfony/proxy.json
    .wip

    or anything else

    .sf?
    Wildcard

    support
    Multiple
    domains per
    directory
    TLS certificates
    created on demand

    View full-size slide

  36. http://127.0.0.1:7080/

    View full-size slide

  37. HTTPS_PROXY=http://localhost:7080 curl -I https://certif.wip/

    View full-size slide

  38. Workers, daemons, ...

    View full-size slide

  39. Manage long-running commands
    # Run yarn in the background
    $ symfony run -d yarn encore dev --watch
    $ symfony server:log
    $ symfony server:status
    Web server listening on https://127.0.0.1:8001
    Command "/usr/local/bin/yarn encore dev --watch" running with PID 93398
    $ symfony server:stop
    command
    logs
    stops the web server

    and the commands

    View full-size slide

  40. Fully integrated

    with SymfonyCloud

    and optimized for Symfony projects

    View full-size slide

  41. Use the local web server
    Connect SymfonyCloud services
    Browse, it works as you would expect it to!

    View full-size slide

  42. https://demo.com/
    Project hosted on SymfonyCloud
    # Init a project and deploy to prod
    $ symfony project:init
    # Deploy a cluster of containers
    PHP, services like DB, TLS, ...

    for the master branch/production env
    $ symfony deploy
    # Make it live
    $ symfony domain:attach demo.com
    ENV=prod DEBUG=false

    View full-size slide

  43. https://some-bug-xtf33gy-ombjum625h7iw.eu.s5y.io/
    Step 1: Create a branch
    # Create a new Git branch for the code
    # Deploy a new cluster of containers
    # Copy data (DB, files, ...) byte to byte
    $ symfony env:create some-bug
    # Open the new deployed URL
    # Exact copy of production
    $ symfony open:remote
    # Debug logs
    $ symfony log
    # Or switch to dev/debug with
    $ symfony debug
    Exact copy of
    production
    ENV=prod DEBUG=false

    View full-size slide

  44. https://demo.com.wip/
    Step 2: Run it locally
    # Attach a local domain for fun
    $ symfony proxy:domain:attach demo.com
    # Run the local web server
    $ symfony server:start -d
    # Open the local website
    $ symfony open:local
    ENV=dev DEBUG=true

    View full-size slide

  45. https://demo.com.wip/
    Step 3: Connect the remote services
    # Open a tunnel with the "some-bug" env
    $ symfony tunnel:open
    # Refresh the page in the browser
    # It works!
    ENV=dev DEBUG=true

    View full-size slide

  46. .env
    APP_ENV=dev
    APP_DEBUG=1
    APP_SECRET=67d829bf61dc5f87a73fd814e2c9f629
    DATABASE_URL=sqlite:///%kernel.project_dir%/data/database.sqlite
    MAILER_URL=null://localhost
    47
    How does it work? Environment variables FTW!
    + The local web server detects the tunnel
    + It exposes the services as env vars in PHP-FPM
    + Symfony and SymfonyCloud share the same convention
    + Symfony reads the env vars
    + You win!

    View full-size slide

  47. Disabled

    for master/prod
    by default

    View full-size slide

  48. prod
    Debugging like a pro
    With production data
    But in a safe isolated environment
    Local
    Web
    prod
    DB

    View full-size slide

  49. prod
    Debugging like a pro
    With production data
    But in a safe isolated environment
    Local
    Web
    prod
    DB
    some-bug
    some-bug
    Web
    DB
    symfony env:create
    Duplicate infra/data
    Create a local Git branch
    1

    View full-size slide

  50. prod
    Debugging like a pro
    With production data
    But in a safe isolated environment
    Local
    Web
    prod
    DB
    DATABASE_URL=postgres://127.0.0.1:30002/main
    symfony tunnel:open
    2
    some-bug
    some-bug
    Web
    DB
    symfony env:create
    Duplicate infra/data
    Create a local Git branch
    1

    View full-size slide

  51. prod
    Debugging like a pro
    With production data
    But in a safe isolated environment
    Local
    Web
    prod
    DB
    DATABASE_URL=postgres://127.0.0.1:30002/main
    symfony tunnel:open
    2
    some-bug
    some-bug
    Web
    symfony server:start
    3
    some-bug
    some-bug
    Web
    DB
    symfony env:create
    Duplicate infra/data
    Create a local Git branch
    1

    View full-size slide

  52. prod
    Debugging like a pro
    With production data
    But in a safe isolated environment
    Local
    Web
    prod
    DB
    DATABASE_URL=postgres://127.0.0.1:30002/main
    symfony tunnel:open
    2
    some-bug
    some-bug
    Web
    symfony server:start
    3
    some-bug
    some-bug
    Web
    DB
    symfony env:create
    Duplicate infra/data
    Create a local Git branch
    1
    symfony env:sync

    View full-size slide

  53. https://some-bug-xtf33gy-ombjum625h7iw.eu.s5y.io/
    Step 4: Fix the issue
    # Fix the code
    # Update the DB schema
    # Change/Add some services
    $ git commit ...
    # Deploy on the remote env
    $ symfony deploy
    # Check that everything is fine
    $ symfony open:remote
    # If the fix takes some time
    # Sync back the data from prod
    $ symfony env:sync
    ENV=prod DEBUG=false

    View full-size slide

  54. https://demo.com/ === https://some-bug-xtf33gy-ombjum625h7iw.eu.s5y.io/
    Step 5: Deploy with confidence
    # Merge everything back (code + infra)
    to production
    $ git checkout master
    $ git merge some-bug
    # Deploy production
    # Fast -> same container images
    $ symfony deploy
    ENV=prod DEBUG=false

    View full-size slide

  55. Bonus: Works in a terminal as well
    $ symfony tunnel:open
    $ symfony php -r 'echo $_SERVER["DATABASE_URL"];'
    postgres://main:[email protected]:30002/main?sslmode=disable&charset=utf8&serverVersion=10
    # Env vars are useful with bin/console as well
    $ symfony console ...
    # Works for "some" other commands as well (connects to the SymfonyCloud DB)
    $ symfony run psql
    SymfonyCloud
    Environment
    variables are set

    View full-size slide

  56. My ideal setup!
    Local Web Server and local files

    Remote services

    Works well on the major platforms
    Fast and convenient

    View full-size slide

  57. https://symfony.com/cloud/download
    Test it today!

    View full-size slide