DEV Community

Anders Björk
Anders Björk

Posted on

PHP, markdown and no dependencies

I’ve been thinking about building a travel site called romantic-weekend.com, focused on romantic weekend destinations in Europe and the United States. The basic idea is deliberately simple: people search Google for things like “romantic weekend in Paris”, “romantic weekend in Rome”, “romantic getaway in Napa Valley” or “romantic weekend in France”, and they land directly on a highly focused destination page. The site would contain broader landing pages for regions and countries, such as Europe, France, Italy and the United States, alongside individual destination pages for places like Paris, Venice, New York and San Francisco. Because I expect most visitors to arrive through Google rather than navigate from the homepage, each destination page needs to work well on its own, while still belonging to a clear overall structure.

The more I thought about the technical side of the project, the less interested I became in using a traditional CMS. Romantic-weekend.com is not meant to be a complicated application. It does not need user accounts, comments, dashboards, workflows, permissions or a complex database model. At its core, it is an editorial website made up of structured pages. That made me wonder whether the simplest possible architecture might also be the best one: PHP for the site engine, Markdown files for the content, the filesystem for the hierarchy and ordinary PHP templates for the output.

I imagine the content directory looking something like this:

/content
    /europe
        index.md
        /france
            index.md
            paris.md
            nice.md
            annecy.md
        /italy
            index.md
            rome.md
            venice.md

    /usa
        index.md
        /california
            index.md
            san-francisco.md
            napa-valley.md
        /new-york
            index.md
            new-york-city.md
Enter fullscreen mode Exit fullscreen mode

The nice thing about this structure is that the filesystem itself describes the site. A file at /content/europe/france/paris.md naturally becomes the page /europe/france/paris/, while /content/europe/france/index.md becomes /europe/france/. I don’t need a database table that says Paris belongs to France and France belongs to Europe, because the location of the file already tells me that. I also don’t need to repeat the URL, the parent page, the country and the region in every content file. PHP can derive most of that automatically from the path.

A Paris page could then be very simple:

---
title: "Romantic weekend in Paris"
description: "Plan a romantic weekend in Paris."
image: paris.jpg
---

# Romantic weekend in Paris

Paris is one of the classic destinations for a romantic weekend.

## Where to stay

...

## Romantic things to do

...

## Where to eat

...
Enter fullscreen mode Exit fullscreen mode

The Markdown file only describes the content. PHP handles everything around it: the URL, breadcrumbs, metadata, templates, related destinations, canonical URLs, sitemap entries and navigation. That separation is important to me because I want the content files to stay readable even if the site becomes quite large. If romantic-weekend.com eventually contains hundreds of destinations, I still want adding a new one to be as simple as creating a new Markdown file in the correct directory.

For example, adding Lyon should ideally mean creating /content/europe/france/lyon.md and nothing else. The France page should automatically discover it. The sitemap should automatically include it. The breadcrumb should automatically become “Europe › France › Lyon”. A “More romantic weekends in France” section should automatically be able to link to it. The system should understand the hierarchy without me having to maintain the same information in five different places.

That is also why I like the idea of country and region pages being proper content pages rather than just technical category archives. /europe/ could target searches around romantic weekend destinations in Europe. /europe/france/ could target romantic weekends in France. /usa/california/ could target romantic weekend getaways in California. These pages could contain their own editorial introductions, recommendations and internal links, while PHP automatically inserts the relevant child destinations. This gives the site a clear SEO structure without forcing visitors to navigate through every level before reaching the page they actually want.

My first instinct for handling Markdown was to use a mature package such as league/commonmark. That would certainly work, and for many projects it would be the sensible choice. But romantic-weekend.com made me question whether I really need a complete Markdown implementation at all. I control every content file myself. There are no users pasting arbitrary Markdown into a form. I do not need to support every edge case in the CommonMark specification. In practice, the editorial content probably only needs headings, paragraphs, bold and italic text, links, lists, blockquotes and maybe images.

That means I could write a deliberately small Markdown parser instead of pulling in a dependency. The distinction matters: I would not be trying to write a standards-compliant Markdown parser. I would be defining a small markup format for this specific website that happens to use familiar Markdown syntax. If the site supports #, ##, **bold**, [links](...) and simple lists, that is the specification. Anything more complicated simply does not belong in the content format unless I consciously decide to add it later.

The same goes for the metadata block at the top of each file. I do not need a complete YAML parser just to read three or four simple values. A lightweight parser that reads key: value lines is probably enough. That keeps the project extremely portable. In the simplest version, the entire runtime dependency stack could be nothing more than PHP itself.

That idea appeals to me more than I expected. The Markdown files become the database. Git becomes the version history. The directory tree becomes the taxonomy. PHP becomes the rendering engine. If I move the site to another server, I copy the project and it works. There is no database dump to import, no CMS installation to upgrade and no long dependency tree that needs to be restored before the site can even render a page.

I would probably still build an index of all the content files rather than recursively scanning the filesystem on every request. A small build command could walk through /content, read the metadata from every page and generate a PHP array containing URLs, titles, file paths, parent relationships and perhaps some tags. That index could then power routing, breadcrumbs, country listings, related destinations and the XML sitemap.

The generated index might contain entries like this:

return [
    '/europe/france/paris/' => [
        'title' => 'Romantic weekend in Paris',
        'file' => 'europe/france/paris.md',
        'type' => 'destination',
    ],

    '/europe/france/lyon/' => [
        'title' => 'Romantic weekend in Lyon',
        'file' => 'europe/france/lyon.md',
        'type' => 'destination',
    ],
];
Enter fullscreen mode Exit fullscreen mode

At that point, routing becomes trivial. PHP receives /europe/france/paris/, finds the matching entry, reads the file, converts the Markdown, passes the result through the destination template and returns the HTML. If performance ever matters, I could cache the fully rendered page and serve that cached HTML directly until the source Markdown changes. The end result would behave almost like a static site while still giving me the flexibility of PHP.

One of the more interesting possibilities is to go slightly beyond ordinary Markdown and add a few site-specific constructs. Internal links, for example, could use identifiers rather than hard-coded URLs. Instead of writing Paris, I could write [[paris]] and let PHP resolve that identifier to the current URL. If I ever reorganize the URL structure, the content would not need to change. The same approach could work for dynamic sections such as [[related]], [[destinations]] or [[hotels]], where PHP replaces the token with a generated component.

That would turn the content format into a tiny domain-specific language for romantic-weekend.com. The Markdown would still be readable as plain text, but it would also be able to express relationships that are meaningful to this particular site. A France landing page might contain its editorial text followed by [[destinations]], and PHP would automatically insert all French destinations. A Paris page might include [[related]], and the engine could select other destinations based on geography, tags or manually supplied relationships.

I also like the idea of storing structured attributes in the content files that are useful for recommendations rather than just presentation. A destination could be tagged as city, food, culture, luxury, beach or countryside, and could have values for best months, typical trip length or general price level. Those fields would make it possible to create useful internal links such as “More romantic city weekends”, “Romantic destinations for autumn” or “Similar destinations to Paris” without manually maintaining those sections on every page.

The main thing I want to avoid is turning this small system into a framework of its own. That would defeat the purpose. If the Markdown parser starts trying to support every corner of the CommonMark specification, I should use a proper Markdown library. If the metadata format starts growing into a complex schema language, I should probably use a real parser. If the site eventually needs multiple editors, workflows, permissions and a large editorial backend, then a CMS may become the right answer.

But for the site I have in mind right now, those problems do not exist. Romantic-weekend.com is fundamentally a collection of editorial landing pages organized into a predictable hierarchy. Most visitors will probably enter through Google on a specific destination page, so the real challenge is not application complexity. It is creating good pages, organizing them sensibly and making the relationships between them useful to both search engines and visitors.

For that problem, I find the idea of a very small PHP system surprisingly compelling. A few classes, a directory full of Markdown files, some templates, a generated index, an XML sitemap and an HTML cache may be all the infrastructure the site actually needs.

There is something refreshing about a system where I can understand the whole thing by opening the project folder. No hidden database structure. No plugin ecosystem. No framework conventions I need to remember. No package that suddenly requires a newer version of another package.

Just files in, HTML out.

For romantic-weekend.com, that might be exactly the right level of technology.

Top comments (0)