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

Enough about Classes, Let's Talk Templates

Enough about Classes, Let's Talk Templates


Jonathan Reinink

May 20, 2015

More Decks by Jonathan Reinink

Other Decks in Technology


  1. None
  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
  3. Why do templates matter?

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

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

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

  8. Templates are always "spaghetti code". Because contain more than one

    language in the same file.
  9. Bad template code contributes to technical debt.

  10. Step 1: Choose the right template library.

  11. People have very strong opinions about templating.

  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
  13. “I'm not suggesting that native PHP templates are better than

    compiled templates (like Twig).” —Me
  14. “oh... puts the pitchfork down” —Friendly Reddit reader “hides torch

    behind back…” —Another friendly Reddit reader
  15. The native PHP vs compiled templates debate.

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

    PHP is a templating language)
  17. <html> ! <h1>Hello, <?=$name?></h1> ! <ul> <?php if ($stmt =

    $mysqli->prepare("SELECT name FROM friends")) { $stmt->execute(); $stmt->bind_result($name); ! while ($stmt->fetch()) { echo '<li>'.$name.'</li>'; } } ?> </ul>
  18. Important lesson: If your templates contain SQL code, you’re doing

    it wrong.
  19. While PHP has evolved into a mature, object oriented language,

    it hasn’t improved much as a templating language.
  20. Compiled templates fill this void by offering a new syntax

    specifically geared for templating.
  21. {% extends "template.html" %} ! {% block title %}User Profile{%

    endblock %} ! {% block content %} <h1>User Profile</h1> <p>Hello, {{ name }}</p> {% endblock %}
  22. Since these compiled templates must be compiled, there is a

    slight performance hit.
  23. You can write good template code with most modern templating

    libraries. Twig, Blade, Smarty, Moustache, Plates, Aura.View, Zend\View, etc…
  24. I recommend you use Twig.

  25. Choose a templating language with an easy learning curve. Easy

    for both server-side developers and front-end developers.
  26. Compiled languages offer very simple, template oriented syntax. {{ var

  27. <?php if ($items): ?> <?php foreach ($items as $item): ?>

    <li><?php echo $item ?></li> <?php endforeach ?> <?php else: ?> <li>No items found.</li> <?php endif ?> {% for item in items %} <li>{{ item }}</li> {% else %} <li>No items found.</li> {% endfor %} Native PHP Twig
  28. But, native PHP templates don’t have to be ugly.

  29. Always use the short echo syntax. <?php echo $name; ?>

  30. Never use blocks of PHP. <?php foreach ($users as $user)

    { echo $user->name; } ?> <?php foreach ($users as $user): ?> <?=$user->name?> <?php endforeach ?>
  31. Only one statement per PHP tag. <?php if ($users) {

    foreach ($users as $user) { ?> <?=$user->name?> <?php } } ?> <?php if ($users): ?> <?php foreach ($users as $user): ?> <?=$user->name?> <?php endforeach ?> <?php endif ?>
  32. Never use curly brackets. <?php foreach ($users as $user) {

    ?> <?=$user->name?> <?php } ?> <?php foreach ($users as $user): ?> <?=$user->name?> <?php endforeach ?>
  33. Never using semicolons. <?php echo $name; ?> <?=$user->name?>

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

    ! <h2>Friends</h2> <ul> <?php foreach($friends as $friend): ?> <li> <a href="/profile/<?=$this->e($friend->id)?>"> <?=$this->e($friend->name)?> </a> </li> <?php endforeach ?> </ul> ! <?php if ($invitations): ?> <h2>Invitations</h2> <p>You have some friend invites!</p> <?php endif ?>
  35. Avoid XML attribute based templating languages.

  36. <div class="item" tal:repeat="value values"> <div class="title"> <span tal:condition="value/hasDate" tal:replace="value/getDate"/> <a

    tal:attributes="href value/getUrl" tal:content="value/getTitle"/> </div> <div id="content" tal:content="value/getContent"/> </div>
  37. Choose a templating language with syntax highlighting and auto- completion

    in your editor of choice.
  38. Always, always, always escape unsafe data.

  39. Hello, <?=$name?>

  40. Hello, Jonathan

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

  42. Hello, &lt;a href=&quot;http://xssattack.com/ &quot;&gt;Click here&lt;/a&gt;gt;

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

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

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

  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:
  47. If at all possible, use automatic escaping!

  48. Hello, {{ name }}

  49. Without automatic escaping, you MUST manually escape every single variable.

    And you will probably forget once in a while!!!
  50. It should also be VERY obvious in your templates when

    you are outputting raw data.
  51. Hello, {!! $name !!} Hello, {{ name|raw }} Blade Twig

  52. Automatic escaping is NOT possible in native PHP.

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

  54. class Template { protected $values; ! public function __get($name) {

    return $this->escape( $this->values[$name] ); } }
  55. Problem: How do you escape object params and method calls?

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

  57. class Template { protected $values; ! public function __get($name) {

    return new TemplateProxy( $this->values[$name] ); } }
  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); } }
  59. <h1><?=$this->user->name?></h1>

  60. None
  61. Problems: double escaping, poor performance, horrible error debugging, collisions with

    template methods, etc, etc.
  62. Attempt 3: Compiled native php templates

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

  64. None
  65. Automatic escaping is NOT possible in native PHP.

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

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

  68. Watch your indenting.

  69. <ul> {% for item in items %} <li>{{ item }}</li>

    {% else %} <li>No items found.</li> {% endfor %} </ul>
  70. Use comments that only appear in the template, not the

    rendered markup.
  71. {# This is a Twig comment #}

  72. Your templates should be very low on logic.

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

    complex tasks.
  74. Anything outside of conditionals, loops and basic formatting is a

    code smell.
  75. Watch your control structures. Use: if, for, foreach Don’t use:

    switch, while, do-while
  76. So what exactly is basic formatting then? • Date formatting

    • Case changes • String concatenation • String trimming • Number formatting • URL encoding
  77. Consider using a logic- less templating language.

  78. <ul> {{# users }} <li>{{ name }}</li> {{/ users }}

  79. namespace App\ViewModels; ! class UsersViewModel { public function users() {

    return [ ['name' => 'Jonathan'], ['name' => 'Amy'], ['name' => 'Joey'], ]; } }
  80. Some template logic is okay, but only if it’s presentation

  81. Avoid accessing the global namespace.

  82. @if (Request::is('/')) <a class="active" href="/">Home</a> @else <a href="/">Home</a> @endif

  83. @if ($url === '/') <a class="active" href="/">Home</a> @else <a href="/">Home</a>

  84. Preassign template data instead of accessing global variables.

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

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

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

  88. Use the dot notation for variable resolution. object.parameter Access arrays,

    objects and methods the same way.
  89. {{ user.name }} = $user['name'] {{ user.name }} = $user->name

    {{ user.name }} = $user->name()
  90. Templates should only know about one instance variable.

  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
  92. Keep your templates as short as possible. Break templates into

    smaller, reusables pieces.
  93. <?php $this->insert('partials/header') ?> ! <p>Your page content.</p> ! <?php $this->insert('partials/footer')

  94. <?php $this->insert('partials/header', ['title' => 'Home']) ?> ! <p>Your page content.</p>

    ! <?php $this->insert('partials/footer') ?>
  95. {% include 'partials/header.html' with {'title': 'Home'} %} ! <p>Your page

    content.</p> ! {% include 'partials/footer.html' %}
  96. Don't be afraid to repeat (very) small pieces of code.

  97. <select> <?php foreach ($users as $user): ?> <option <?php if

    ($current_user === $user) echo 'selected' ?> value="<?=$user->id?>"> <?=$user->name?> </option> <?php endforeach ?> </select>
  98. <select> <?php foreach ($users as $user): ?> <?php if ($current_user

    === $user): ?> <option selected value="<?=$user->id?>"><?=$user->name?></option> <?php else ?> <option value="<?=$user->id?>"><?=$user->name?></option> <?php endif ?> <?php endforeach ?> </select>
  99. Use inheritance for even better organization of templates.

  100. template.html {% extends "template.html" %} ! {% block title %}Profile{%

    endblock %} {% block content %} <h1>Profile</h1> <p>Hello, {{ name }}</p> {% endblock %} profile.html <html> <head> <title> {% block title %} Default title {% endblock %} </title> </head> <body> ! <main> {% block content %} <p>Default content</p> {% endblock %} </main> ! </body> </html>
  101. Use a templating language that you can extend.

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

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

    }); @datetime($blog->publish_date)
  104. Use a template language that’s easy to debug.

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

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

    all do.
  107. Automatic escaping is WAY more important than the speed advantage

    of native PHP templates.
  108. Follow me on Twitter at @reinink Rate this talk joind.in/15753