惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Attack and Defense Labs
Attack and Defense Labs
N
News and Events Feed by Topic
L
LINUX DO - 热门话题
PCI Perspectives
PCI Perspectives
www.infosecurity-magazine.com
www.infosecurity-magazine.com
爱范儿
爱范儿
D
DataBreaches.Net
Simon Willison's Weblog
Simon Willison's Weblog
S
Secure Thoughts
S
SegmentFault 最新的问题
博客园 - 【当耐特】
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
博客园 - 叶小钗
P
Proofpoint News Feed
The Hacker News
The Hacker News
T
ThreatConnect
N
News and Events Feed by Topic
T
Threatpost
The Register - Security
The Register - Security
WordPress大学
WordPress大学
博客园 - Franky
Recorded Future
Recorded Future
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
Project Zero
Project Zero
大猫的无限游戏
大猫的无限游戏
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
罗磊的独立博客
Stack Overflow Blog
Stack Overflow Blog
腾讯CDC
F
Future of Privacy Forum
F
Full Disclosure
Cyberwarzone
Cyberwarzone
J
Java Code Geeks
李成银的技术随笔
Schneier on Security
Schneier on Security
Know Your Adversary
Know Your Adversary
H
Hacker News: Front Page
人人都是产品经理
人人都是产品经理
博客园_首页
Scott Helme
Scott Helme
Google DeepMind News
Google DeepMind News
美团技术团队
Malwarebytes
Malwarebytes
Last Week in AI
Last Week in AI
T
Tailwind CSS Blog
T
The Exploit Database - CXSecurity.com
G
GRAHAM CLULEY
Recent Announcements
Recent Announcements
C
CXSECURITY Database RSS Feed - CXSecurity.com

CSS-Tricks

Revealing Text With CSS letter-spacing | CSS-Tricks Technical Writing in the AI Age | CSS-Tricks Cross-Document View Transitions: Scaling Across Hundreds of Elements | CSS-Tricks Cross-Document View Transitions: Scaling Across Hundreds of Elements | CSS-Tricks The State of CSS Centering in 2026 | CSS-Tricks Stack Overflow: When We Stop Asking | CSS-Tricks Cross-Document View Transitions: The Gotchas Nobody Mentions | CSS-Tricks What’s !important #11: 3D Voxel Scenes, Flying Focus, CSS Syntaxes, and More | CSS-Tricks Computing and Displaying Discounted Prices in CSS | CSS-Tricks rotateX() | CSS-Tricks rotateY() | CSS-Tricks rotateZ() | CSS-Tricks rotate() | CSS-Tricks Soon We Can Finally Banish JavaScript to the ShadowRealm | CSS-Tricks Using CSS corner-shape For Folded Corners | CSS-Tricks A Scrollytelling Gift for Mum on Mother’s Day 2026 | CSS-Tricks Google’s Prompt API | CSS-Tricks Making Zigzag CSS Layouts With a Grid + Transform Trick | CSS-Tricks Fixed-Height Cards: More Fragile Than They Look | CSS-Tricks What’s !important #10: HTML-in-Canvas, Hex Maps, E-ink Optimization, and More | CSS-Tricks The Importance of Native Randomness in CSS | CSS-Tricks contrast() | CSS-Tricks contrast-color() | CSS-Tricks Let’s Use the Nonexistent ::nth-letter Selector Now | CSS-Tricks Quick Hit #126 Recreating Apple’s Vision Pro Animation in CSS | CSS-Tricks Quick Hit #125 Enhancing Astro With a Markdown Component | CSS-Tricks Quick Hit #124 Markdown + Astro = ❤️ | CSS-Tricks Quick Hit #123 What’s !important #9: clip-path Jigsaws, View Transitions Toolkit, Name-only Containers, and More | CSS-Tricks A Well-Designed JavaScript Module System is Your First Architecture Decision | CSS-Tricks hypot() | CSS-Tricks The Radio State Machine | CSS-Tricks 7 View Transitions Recipes to Try | CSS-Tricks Quick Hit #122 Quick Hit #121 Selecting a Date Range in CSS | CSS-Tricks saturate() | CSS-Tricks justify-self | CSS-Tricks Quick Hit #120 Alternatives to the !important Keyword | CSS-Tricks Quick Hit #119 New CSS Multi-Column Layout Features in Chrome | CSS-Tricks Quick Hit #118 Making Complex CSS Shapes Using shape() | CSS-Tricks Quick Hit #117 Front-End Fools: Top 10 April Fools’ UI Pranks of All Time | CSS-Tricks Sniffing Out the CSS Olfactive API | CSS-Tricks What’s !important #8: Light/Dark Favicons, @mixin, object-view-box, and More | CSS-Tricks Quick Hit #116 Form Automation Tips for Happier User and Clients | CSS-Tricks Quick Hit #115 Generative UI Notes | CSS-Tricks Quick Hit #114 Quick Hit #113 Experimenting With Scroll-Driven corner-shape Animations | CSS-Tricks Quick Hit #112 JavaScript for Everyone: Destructuring | CSS-Tricks Quick Hit #111 Quick Hit #110 What’s !important #7: random(), Folded Corners, Anchored Container Queries, and More | CSS-Tricks 4 Reasons That Make Tailwind Great for Building Layouts | CSS-Tricks Quick Hit #109 Quick Hit #108 Abusing Customizable Selects | CSS-Tricks Quick Hit #107 The Value of z-index | CSS-Tricks Quick Hit #106 The Different Ways to Select <html> in CSS Quick Hit #105 Popover API or Dialog API: Which to Choose? Quick Hit #104 What’s !important #6: :heading, border-shape, Truncating Text From the Middle, and More Yet Another Way to Center an (Absolute) Element An Exploit ... in CSS?! Quick Hit #103 A Complete Guide to Bookmarklets Quick Hit #102 Loading Smarter: SVG vs. Raster Loaders in Modern Web Design Potentially Coming to a Browser :near() You Quick Hit #101 Distinguishing "Components" and "Utilities" in Tailwind Quick Hit #100 Spiral Scrollytelling in CSS With sibling-index() Interop 2026 Quick Hit #99 What’s !important #5: Lazy-loading iframes, Repeating corner-shape Backgrounds, and More Quick Hit #98 Making a Responsive Pyramidal Grid With Modern CSS Approximating contrast-color() With Other CSS Features Quick Hit #97 Trying to Make the Perfect Pie Chart in CSS Quick Hit #96 Quick Hit #95 CSS Bar Charts Using Modern Functions Quick Hit #94 No Hassle Visual Code Theming: Publishing an Extension Quick Hit #93
PHP is A-OK for Templating
CSS-Tricks · 2020-02-05 · via CSS-Tricks

PHP templating often gets a bad rap for facilitating subpar code — but that doesn’t have to be the case. Let’s look at how PHP projects can enforce a basic Model, View, Controller (MVC) structure without depending on a purpose-built templating engine.

But first, a very brief PHP history lesson

The history of PHP as a tool for HTML templating is full of twists and turns.

One of the first programming languages used for HTML templating was C, but it was quickly found to be tedious to use and generally ill-suited for the task.

Rasmus Lerdorf created PHP with this in mind. He wasn’t opposed to using C to handle back-end business logic but wanted a better way to generate dynamic HTML for the front end. PHP was originally designed as a templating language, but adopted more features over time and eventually became a full programming language in its own right.

PHP’s unique ability to switch between programming mode and HTML mode was found to be quite convenient, but also made it tempting for programmers to write unmaintainable code — code that mixed business logic and templating logic. A PHP file could begin with some HTML templating and then suddenly dive into an advanced SQL query without warning. This structure is confusing to read and makes reusing HTML templates difficult.

As time passed, the web development community found more and more value enforcing a strict MVC structure for PHP projects. Templating engines were created as a way to effectively separate views from their controllers.

To accomplish this task, templating engines typically have the following characteristics:

  1. The engine is purposely underpowered for business logic. If a developer wants to perform a database query, for example, they need to make that query in the controller and then pass the result to the template. Making a query in the middle of the template’s HTML is not an option.
  2. The engine takes care of common security risks behind the scenes. Even if a developer fails to validate user input and passes it directly into the template, the template will often escape any dangerous HTML automatically.

Templating engines are now a mainstay feature in many web technology stacks. They make for more maintainable, more secure codebases, so this comes as no surprise.

It is possible, however, to handle HTML templating with plain PHP as well. You may want to do this for a number of reasons. Maybe you are working on a legacy project and don’t want to bring in any additional dependencies, or maybe you are working on a very small project and prefer to keep things as lightweight as possible. Or maybe the decision isn’t up to you at all.

Use cases for PHP templating

One place that I often implement plain PHP templating is WordPress, which doesn’t enforce a rigid MVC structure by default. I feel that bringing in a full templating engine is a bit heavy-handed, but I still want to separate my business logic from my templates and want my views to be reusable.

Whatever your reason, using plain PHP to define your HTML templates is sometimes the preferred choice. This post explores how this can be done in a reasonably professional way. The approach represents a practical middle ground between the spaghetti-coded style that PHP templating has become notorious for and the no-logic-allowed approach available with formal templating engines.

Let’s dive into an example of how a basic templating system can be put into practice. Again, we’re using WordPress as an example, but this could be swapped to a plain PHP environment or many other environments. And you don’t need to be familiar with WordPress to follow along.

The goal is to break our views into components and create a distinct separation between the business logic and HTML templates. Specifically, we are going to create a view that displays a grid of cards. Each card is going to display the title, excerpt, and author of a recent post.

Step 1: Fetching data to render

The first step to take is fetching the data that we want to display in our view. This could involve executing a SQL query or using the ORM or helper functions of your framework/CMS to access your database indirectly. It could also involve making an HTTP request to an external API or collecting user input from a form or query string.

In this example, we’re going to use the WordPress get_posts helper function to fetch some posts to display on our homepage.

<?php // index.php
$wp_posts = get_posts([
  'numberposts' => 3
]);

We now have access to the data we want to display in the cards grid, but we need to do some additional work before we can pass it to our view.

Step 2: Preparing data for templating

The get_posts function returns an array of WP_Post objects. Each object contains the post title, excerpt, and author information that we need, but we don’t want to couple our view to the WP_Post object type because we might want to show other kinds of data on our cards somewhere else in the project.

Instead, it makes sense to proactively convert each post object to a neutral data type, like an associative array:

<?php // index.php
$wp_posts = get_posts([
  'numberposts' => 3
]);

$cards = array_map(function ($wp_post) {
  return [
    'heading' => $wp_post->post_title,
    'body' => $wp_post->post_excerpt,
    'footing' => get_author_name($wp_post->post_author)
  ];
}, $wp_posts);

In this case, each WP_Post object is converted into an associative array by using the array_map function. Notice that the keys for each value are not title, excerpt, and author but are given more general names instead: heading, body, and footing. We do this because the cards grid component is meant to support any kind of data, not just posts. It could just as easily be used to show a grid of testimonials that have a quote and a customer’s name, for example.

With the data properly prepared, it can now be passed into our render_view function:

<?php // index.php
// Data fetching and formatting same as before

render_view('cards_grid', [
  'cards' => $cards
]);

Of course, the render_view function does not exist yet. Let’s define it.

Step 3: Creating a render function

// Defined in functions.php, or somewhere else that will make it globally available.
// If you are worried about possible collisions within the global namespace,
// you can define this function as a static method of a namespaced class
function render_view($view, $data)
{
  extract($data);
  require('views/' . $view . '.php');
}

This function accepts the name of the rendered view and an associative array representing any data to be displayed. The extract function takes each item in the associative array and creates a variable for it. In this example, we now have a variable named $cards that contains the items we prepared in index.php.

Since the view is executed in its own function, it gets its own scope. This is nice because it allows us to use simple variable names without fear of collisions.

The second line of our function prints the view matching the name passed. In this case, it looks for the view in views/cards_grid.php. Let’s go ahead and create that file.

Step 4: Creating templates

<?php /* views/cards_grid.php */ ?>
<section>
  <ul>
    <?php foreach ($cards as $card) : ?>
    <li>
      <?php render_view('card', $card) ?>
    </li>
    <?php endforeach; ?>    
  </ul>
</section>

This template uses the $cards variable that was just extracted and renders it as an unordered list. For each card in the array, the template renders a subview: the singular card view.

Having a template for a single card is useful because it gives us the flexibility to render a single card directly or use it in another view somewhere else in the project.

Let’s define the basic card view:

<?php /* views/card.php */ ?>
<div class="card">
  <?php if (!empty($heading)) : ?>
    <h4><?= htmlspecialchars($heading) ?></h4>      
  <?php endif;
  if (!empty($body)) : ?>
    <p><?= htmlspecialchars($body) ?></p>      
  <?php endif;
  if (!empty($footing)) : ?>      
    <span><?= htmlspecialchars($footing) ?></span>
  <?php endif; ?>
</div>

Since the $card that was passed into the render function contained keys for a heading, body, and footing, variables of those same names are now available in the template.

In this example, we can be reasonably sure that our data is free of XSS hazards, but it’s possible that this view could be used with user input at some later time, so passing each value through htmlspecialchars is prudent. If a script tag exists in our data it will be safely escaped.

It’s also often helpful to check that each variable contains a non-empty value before rendering it. This allows for variables to be omitted without leaving empty HTML tags in our markup.


PHP templating engines are great but it is sometimes appropriate to use PHP for what it was originally designed for: generating dynamic HTML.

Templating in PHP doesn’t have to result in unmaintainable spaghetti code. With a little bit of foresight we can achieve a basic MVC system that keeps views and controllers separate from each other, and this can be done with surprisingly little code.