$30 off During Our Annual Pro Sale. View Details »

Enough about Classes, Let's Talk Templates

Enough about Classes, Let's Talk Templates

Jonathan Reinink

May 20, 2015
Tweet

More Decks by Jonathan Reinink

Other Decks in Technology

Transcript

  1. View Slide

  2. Jonathan Reinink
    Software developer from Canada
    Been writing PHP for about 15 years
    Marketing agency for over a decade
    Started contract development this year
    Leadership group of The PHP League
    Wrote the Plates template library
    Wrote the Glide image library
    I <3 open source

    View Slide

  3. Why do templates matter?

    View Slide

  4. Templates often account for
    25-50% of your overall code base.

    View Slide

  5. Templates help separate your
    controller and domain logic from
    your presentation logic.

    View Slide

  6. View Slide

  7. Templates are often developed
    by both server-side developers,
    and front-end developers.

    View Slide

  8. Templates are always
    "spaghetti code".
    Because contain more than one language in the same file.

    View Slide

  9. Bad template code
    contributes to technical debt.

    View Slide

  10. Step 1: Choose the
    right template library.

    View Slide

  11. People have very strong
    opinions about templating.

    View Slide

  12. “This blog post is not for the faint-
    hearted! Some people will strongly
    disagree with me and some others
    will probably want to kill me at the
    upcoming Zend Conference.”
    —Fabien Potencier

    View Slide

  13. “I'm not suggesting that native
    PHP templates are better than
    compiled templates (like Twig).”
    —Me

    View Slide

  14. “oh... puts the pitchfork down”
    —Friendly Reddit reader
    “hides torch behind back…”
    —Another friendly Reddit reader

    View Slide

  15. The native PHP vs compiled
    templates debate.

    View Slide

  16. PHP is the oldest method
    of templating in PHP.
    (because PHP is a templating language)

    View Slide


  17. !
    Hello, =$name?>
    !

    if ($stmt = $mysqli->prepare("SELECT name FROM friends")) {
    $stmt->execute();
    $stmt->bind_result($name);
    !
    while ($stmt->fetch()) {
    echo ''.$name.'';
    }
    }
    ?>

    View Slide

  18. Important lesson:
    If your templates contain SQL
    code, you’re doing it wrong.

    View Slide

  19. While PHP has evolved into a
    mature, object oriented language,
    it hasn’t improved much as a
    templating language.

    View Slide

  20. Compiled templates fill this void
    by offering a new syntax
    specifically geared for templating.

    View Slide

  21. {% extends "template.html" %}
    !
    {% block title %}User Profile{% endblock %}
    !
    {% block content %}
    User Profile
    Hello, {{ name }}
    {% endblock %}

    View Slide

  22. Since these compiled templates
    must be compiled, there is a
    slight performance hit.

    View Slide

  23. You can write good template
    code with most modern
    templating libraries.
    Twig, Blade, Smarty, Moustache, Plates, Aura.View, Zend\View, etc…

    View Slide

  24. I recommend you use Twig.

    View Slide

  25. Choose a templating language
    with an easy learning curve.
    Easy for both server-side developers and front-end developers.

    View Slide

  26. Compiled languages offer very
    simple, template oriented syntax.
    {{ var }}

    View Slide






  27. No items found.

    {% for item in items %}
    {{ item }}
    {% else %}
    No items found.
    {% endfor %}
    Native PHP Twig

    View Slide

  28. But, native PHP templates
    don’t have to be ugly.

    View Slide

  29. Always use the
    short echo syntax.
    =$user->name?>

    View Slide

  30. Never use blocks of PHP.
    foreach ($users as $user) {
    echo $user->name;
    }
    ?>

    =$user->name?>

    View Slide

  31. Only one statement per PHP tag.

    =$user->name?>



    =$user->name?>


    View Slide

  32. Never use curly brackets.

    =$user->name?>


    =$user->name?>

    View Slide

  33. Never using semicolons.
    =$user->name?>

    View Slide

  34. layout('template', ['title' => 'User Profile']) ?>
    !
    Hello =$this->e($name)?>
    !
    Friends




    =$this->e($friend->name)?>




    !

    Invitations
    You have some friend invites!

    View Slide

  35. Avoid XML attribute based
    templating languages.

    View Slide








  36. View Slide

  37. Choose a templating language with
    syntax highlighting and auto-
    completion in your editor of choice.

    View Slide

  38. Always, always, always
    escape unsafe data.

    View Slide

  39. Hello, =$name?>

    View Slide

  40. Hello, Jonathan

    View Slide

  41. Hello, Click here

    View Slide

  42. Hello, <a href="http://xssattack.com/
    ">Click here</a>gt;

    View Slide

  43. Hello, Click here

    View Slide

  44. Hello, =htmlspecialchars($name, ENT_QUOTES | ENT_SUBSTITUTE)?>

    View Slide

  45. Hello, =$this->escape($name)?>
    Hello, =$this->e($name)?>
    Hello, =e($name)?>

    View Slide

  46. 1. HTML escaping
    2. HTML attribute escaping
    3. CSS escaping
    4. JavaScript escaping
    5. URL escaping
    Be aware of the different types of escaping:

    View Slide

  47. If at all possible, use
    automatic escaping!

    View Slide

  48. Hello, {{ name }}

    View Slide

  49. Without automatic escaping,
    you MUST manually escape
    every single variable.
    And you will probably forget once in a while!!!

    View Slide

  50. It should also be VERY obvious
    in your templates when you
    are outputting raw data.

    View Slide

  51. Hello, {!! $name !!} Hello, {{ name|raw }}
    Blade Twig

    View Slide

  52. Automatic escaping is NOT
    possible in native PHP.

    View Slide

  53. =$this->name?>
    Attempt 1: Object overloading

    View Slide

  54. class Template
    {
    protected $values;
    !
    public function __get($name)
    {
    return $this->escape(
    $this->values[$name]
    );
    }
    }

    View Slide

  55. Problem: How do you escape object
    params and method calls?
    =$this->user->name?>

    View Slide

  56. Attempt 2: Object
    overloading with proxies
    =$this->user->name?>

    View Slide

  57. class Template
    {
    protected $values;
    !
    public function __get($name)
    {
    return new TemplateProxy(
    $this->values[$name]
    );
    }
    }

    View Slide

  58. class TemplateProxy
    {
    protected $value;
    !
    public function __get($name)
    {
    return new TemplateProxy($this->name);
    }
    !
    public function __call($name, $args)
    {
    return new TemplateProxy(...);
    }
    !
    public function __toString()
    {
    return $this->escape($this->value);
    }
    }

    View Slide

  59. =$this->user->name?>

    View Slide

  60. View Slide

  61. Problems: double escaping, poor
    performance, horrible error
    debugging, collisions with
    template methods, etc, etc.

    View Slide

  62. Attempt 3: Compiled
    native php templates

    View Slide

  63. =$name?>
    =$this->escape($name)?>
    Pre-compilation:
    Post-compilation:

    View Slide

  64. View Slide

  65. Automatic escaping is NOT
    possible in native PHP.

    View Slide

  66. Watch your whitespace, this
    isn’t the Wild Wild West.

    View Slide

  67. Always use tabs, or always
    use spaces, NEVER MIX.

    View Slide

  68. Watch your indenting.

    View Slide


  69. {% for item in items %}
    {{ item }}
    {% else %}
    No items found.
    {% endfor %}

    View Slide

  70. Use comments that only
    appear in the template,
    not the rendered markup.

    View Slide

  71. {# This is a Twig comment #}

    View Slide

  72. Your templates should
    be very low on logic.

    View Slide

  73. Templates are not responsible
    for data lookup, persistence or
    other complex tasks.

    View Slide

  74. Anything outside of
    conditionals, loops and basic
    formatting is a code smell.

    View Slide

  75. Watch your
    control structures.
    Use:
    if, for, foreach
    Don’t use:
    switch, while, do-while

    View Slide

  76. So what exactly is basic
    formatting then?
    • Date formatting
    • Case changes
    • String concatenation
    • String trimming
    • Number formatting
    • URL encoding

    View Slide

  77. Consider using a logic-
    less templating language.

    View Slide


  78. {{# users }}
    {{ name }}
    {{/ users }}

    View Slide

  79. namespace App\ViewModels;
    !
    class UsersViewModel
    {
    public function users()
    {
    return [
    ['name' => 'Jonathan'],
    ['name' => 'Amy'],
    ['name' => 'Joey'],
    ];
    }
    }

    View Slide

  80. Some template logic is
    okay, but only if it’s
    presentation logic.

    View Slide

  81. Avoid accessing the
    global namespace.

    View Slide

  82. @if (Request::is('/'))
    Home
    @else
    Home
    @endif

    View Slide

  83. @if ($url === '/')
    Home
    @else
    Home
    @endif

    View Slide

  84. Preassign template data
    instead of accessing
    global variables.

    View Slide

  85. $templates->addData(['url' => $url], 'header');

    View Slide

  86. view()->composer('header', function ($view) {
    $view->with('url', $url);
    });

    View Slide

  87. $twig->addGlobal('url', $url);

    View Slide

  88. Use the dot notation
    for variable resolution.
    object.parameter
    Access arrays, objects and methods the same way.

    View Slide

  89. {{ user.name }} = $user['name']
    {{ user.name }} = $user->name
    {{ user.name }} = $user->name()

    View Slide

  90. Templates should only know
    about one instance variable.

    View Slide

  91. Rule #4: Controllers can instantiate only
    one object. Therefore, views can only know
    about one instance variable and views
    should only send messages to that object.
    — Sandi Metz, rules for developers

    View Slide

  92. Keep your templates
    as short as possible.
    Break templates into smaller, reusables pieces.

    View Slide

  93. insert('partials/header') ?>
    !
    Your page content.
    !
    insert('partials/footer') ?>

    View Slide

  94. insert('partials/header', ['title' => 'Home']) ?>
    !
    Your page content.
    !
    insert('partials/footer') ?>

    View Slide

  95. {% include 'partials/header.html' with {'title': 'Home'} %}
    !
    Your page content.
    !
    {% include 'partials/footer.html' %}

    View Slide

  96. Don't be afraid to repeat
    (very) small pieces of code.

    View Slide



  97. 'selected' ?> value="=$user->id?>">
    =$user->name?>



    View Slide




  98. =$user->name?>

    =$user->name?>



    View Slide

  99. Use inheritance for even better
    organization of templates.

    View Slide

  100. template.html
    {% extends "template.html" %}
    !
    {% block title %}Profile{% endblock %}
    {% block content %}
    Profile
    Hello, {{ name }}
    {% endblock %}
    profile.html



    {% block title %}
    Default title
    {% endblock %}



    !

    {% block content %}
    Default content
    {% endblock %}

    !


    View Slide

  101. Use a templating language
    that you can extend.

    View Slide

  102. $templates->registerFunction('upper', function ($string) {
    return strtoupper($string);
    });
    Hello =$this->upper($name)

    View Slide

  103. Blade::directive('datetime', function ($expression) {
    return "format('m/d/Y H:i'); ?>";
    });
    @datetime($blog->publish_date)

    View Slide

  104. Use a template language
    that’s easy to debug.

    View Slide

  105. Use a template language
    that offers a sandbox mode.

    View Slide

  106. Use a template language
    that performs well.
    Hint: they basically all do.

    View Slide

  107. Automatic escaping is WAY more
    important than the speed
    advantage of native PHP templates.

    View Slide

  108. Follow me on Twitter at @reinink
    Rate this talk joind.in/15753
    Thanks!

    View Slide