DEV Community

Russell Jones
Russell Jones

Posted on • Originally published at jonesrussell.github.io on

PSR-13: Hypermedia Links in PHP

Ahnii!

Prerequisites: PHP OOP, REST API basics. Recommended: Read PSR-7 first.

What Problem Does PSR-13 Solve?

Have you ever used a website where you had to manually construct URLs to navigate? Neither have your API consumers — they shouldn't have to either. PSR-13 lets your API tell clients "here's where to go next," like how a website has navigation links.

This is the heart of HATEOAS (Hypermedia as the Engine of Application State) — a REST principle where API responses include links to related actions and resources. Instead of hardcoding /api/users/123/posts in your frontend, the API response itself says "here's the link to this user's posts."

When Would You Actually Use This?

  • Pagination: Include next, prev, first, last links in list responses
  • Related resources: A blog post response includes links to its author, comments, and category
  • Available actions: A draft post includes a publish link; a published post includes unpublish

Today we'll explore PSR-13, which defines interfaces for creating and managing hypermedia links in PHP applications. This standard is particularly useful for building REST APIs that follow HATEOAS (Hypermedia as the Engine of Application State) principles, enabling self-documenting and discoverable APIs.

Core Interfaces

PSR-13 defines three main interfaces that work together to create a flexible hypermedia linking system.

1. LinkInterface

<?php

namespace Psr\Link;

interface LinkInterface
{
    public function getHref();
    public function isTemplated();
    public function getRels();
    public function getAttributes();
}
Enter fullscreen mode Exit fullscreen mode

2. EvolvableLinkInterface

<?php

namespace Psr\Link;

interface EvolvableLinkInterface extends LinkInterface
{
    public function withHref($href);
    public function withRel($rel);
    public function withoutRel($rel);
    public function withAttribute($attribute, $value);
    public function withoutAttribute($attribute);
}
Enter fullscreen mode Exit fullscreen mode

3. LinkProviderInterface

<?php

namespace Psr\Link;

interface LinkProviderInterface
{
    public function getLinks();
    public function getLinksByRel($rel);
}
Enter fullscreen mode Exit fullscreen mode

Usage Examples

1. Basic Link Creation

<?php

// Create a simple link to a user profile
$link = new HypermediaLink('/users/123')
    ->withRel('self')
    ->withAttribute('title', 'User Profile');

// Create a templated link for user resources
$link = new HypermediaLink('/users/{id}')
    ->withRel('user')
    ->withAttribute('templated', true);

// Create a link collection and add links to it
$provider = new HypermediaLinkProvider();
$provider->addLink($link);
Enter fullscreen mode Exit fullscreen mode

2. REST API Implementation

<?php

class UserController
{
    public function show($id): array
    {
        $user = $this->repository->find($id);
        $links = new HypermediaLinkProvider();

        // Add self-referential link
        $links->addLink(
            (new HypermediaLink("/users/$id"))
                ->withRel('self')
        );

        // Add link to related posts
        $links->addLink(
            (new HypermediaLink("/users/$id/posts"))
                ->withRel('posts')
        );

        return [
            'data' => $user,
            '_links' => $this->serializeLinks($links)
        ];
    }
}
Enter fullscreen mode Exit fullscreen mode

Best Practices

1. Link Relations

// Bad - Using arbitrary relation names
$link->withRel('get-user-stuff');

// Good - Using standard IANA relations
$link->withRel('self')
     ->withRel('next')
     ->withRel('prev');
Enter fullscreen mode Exit fullscreen mode

2. Template Parameters

// Bad - Hardcoded IDs in URLs
$link = new HypermediaLink("/users/123/posts");

// Good - Templated links
$link = (new HypermediaLink("/users/{id}/posts"))
    ->withTemplated(true);
Enter fullscreen mode Exit fullscreen mode

Next Steps

In our next article, we'll explore PSR-14, which defines interfaces for event handling in PHP applications.

Framework Integration

Symfony

Symfony's WebLink component implements PSR-13 for HTTP/2 server push and preloading:

<?php

use Symfony\Component\WebLink\Link;

// Add a preload link for a CSS file
$link = (new Link('preload', '/styles/app.css'))
    ->withAttribute('as', 'style');
Enter fullscreen mode Exit fullscreen mode

Try It Yourself

git clone https://github.com/jonesrussell/php-fig-guide.git
cd php-fig-guide
composer install
composer test -- --filter=PSR13
Enter fullscreen mode Exit fullscreen mode

See src/Link/ for how the blog API adds hypermedia links to post responses.

Resources

Baamaapii

Top comments (0)