JSON kode på en blå skærm
← Tilbage til GuidesBegynder - Mellem

PHP JSON

Komplet guide til JSON i PHP — encode, decode, flags og fejlhåndtering.

Foto: Yancy Min / Unsplash

JSON i PHP

JSON er det mest udbredte format til dataudveksling mellem systemer. PHP har to centrale funktioner: json_encode() der konverterer PHP-værdier til JSON-strings, og json_decode() der parser JSON tilbage til PHP-værdier.

Begge funktioner accepterer flags der kontrollerer adfærd som UTF-8 håndtering, exception-baseret fejlhåndtering og output-format. Korrekt brug af disse flags er forskellen mellem solid produktionskode og kode der fejler stille ved Unicode eller tomme objekter.

json_encode() — PHP til JSON

Konvertér arrays, objekter og primitiver til en JSON-string. Standard-output har escaped Unicode og slashes — brug flags for at få læsbart output.

<?php
$user = [
    'navn'  => 'Søren Andersen',
    'by'    => 'København',
    'roles' => ['admin', 'editor'],
    'aktiv' => true,
];

// Standard output (Unicode escapes til \uXXXX)
echo json_encode($user);
// {"navn":"S\u00f8ren Andersen","by":"K\u00f8benhavn",...}

// Med flags - læsbart output
echo json_encode($user,
    JSON_UNESCAPED_UNICODE
    | JSON_UNESCAPED_SLASHES
    | JSON_PRETTY_PRINT
);

// Output:
// {
//     "navn": "Søren Andersen",
//     "by": "København",
//     "roles": ["admin", "editor"],
//     "aktiv": true
// }

Brug altid JSON_UNESCAPED_UNICODE i Danmark — ellers bliver æ, ø, å til escape-sekvenser i output.

json_decode() — JSON til PHP

Parser en JSON-string. Standard returnerer den et stdClass-objekt. Sæt andet argument til true for at få et associative array i stedet.

<?php
$json = '{"navn":"Anna","alder":34,"by":"Aarhus"}';

// Som object (default)
$user = json_decode($json);
echo $user->navn;   // Anna
echo $user->alder;  // 34

// Som associative array
$userArray = json_decode($json, true);
echo $userArray['navn'];   // Anna

// Nested JSON
$apiResponse = '{
    "data": [
        {"id": 1, "title": "Første post"},
        {"id": 2, "title": "Anden post"}
    ],
    "meta": {"total": 2}
}';

$response = json_decode($apiResponse, true);
foreach ($response['data'] as $post) {
    echo "{$post['id']}: {$post['title']}\n";
}
echo "Total: {$response['meta']['total']}";

Fejlhåndtering — JSON_THROW_ON_ERROR

Som standard returnerer json_decode() blot null ved fejl — samme værdi som JSON-strengen "null". Det er en bug-magnet.

<?php
// FORKERT - fejl forsvinder stille
$data = json_decode($input, true);
if ($data === null) {
    // Var inputtet "null" eller var det en fejl?
    // Du ved det ikke...
}

// KORREKT - PHP 7.3+
try {
    $data = json_decode(
        $input,
        true,
        512,
        JSON_THROW_ON_ERROR
    );
} catch (JsonException $e) {
    error_log("JSON parse fejl: {$e->getMessage()}");
    http_response_code(400);
    exit('Invalid JSON');
}

// Samme flag virker for json_encode
try {
    $json = json_encode(
        $data,
        JSON_THROW_ON_ERROR | JSON_UNESCAPED_UNICODE
    );
} catch (JsonException $e) {
    // Typisk hvis der er rekursive references
    // eller invalid UTF-8 i strings
}

Brug JSON_THROW_ON_ERROR som default i alle nye projekter. Det forvandler stille fejl til synlige exceptions.

JSON i REST API-kontekst

En komplet helper til at læse JSON-input og sende JSON-respons med korrekte headers og fejlhåndtering.

<?php

function readJsonInput(): array {
    $raw = file_get_contents('php://input');

    if ($raw === '' || $raw === false) {
        return [];
    }

    try {
        return json_decode(
            $raw,
            true,
            512,
            JSON_THROW_ON_ERROR
        );
    } catch (JsonException $e) {
        http_response_code(400);
        sendJson([
            'error' => 'Invalid JSON',
            'detail' => $e->getMessage(),
        ]);
        exit;
    }
}

function sendJson(mixed $data, int $status = 200): void {
    http_response_code($status);
    header('Content-Type: application/json; charset=utf-8');

    echo json_encode(
        $data,
        JSON_UNESCAPED_UNICODE
        | JSON_UNESCAPED_SLASHES
        | JSON_THROW_ON_ERROR
    );
}

// Brug i en endpoint
$input = readJsonInput();
$user = createUser($input);
sendJson(['id' => $user->id, 'navn' => $user->navn], 201);

JsonSerializable interface

Implementér JsonSerializable i dine value objects for at kontrollere JSON-output. Det forhindrer at private properties eller interne felter lækker ud.

<?php

class User implements JsonSerializable {
    public function __construct(
        private int $id,
        private string $email,
        private string $passwordHash,
        private DateTimeImmutable $createdAt,
    ) {}

    public function jsonSerialize(): array {
        return [
            'id'         => $this->id,
            'email'      => $this->email,
            'created_at' => $this->createdAt->format('c'),
            // passwordHash udelades automatisk
        ];
    }
}

$user = new User(
    1,
    'henrik@example.dk',
    '$2y$12$...',
    new DateTimeImmutable()
);

echo json_encode($user, JSON_UNESCAPED_UNICODE);
// {"id":1,"email":"henrik@example.dk","created_at":"2026-05-03T20:45:12+02:00"}

JSON Flags Reference

JSON_UNESCAPED_UNICODEBevar æøå i stedet for \uXXXX-escapes
JSON_UNESCAPED_SLASHESEscape ikke / til \/
JSON_PRETTY_PRINTIndrykning og linjeskift i output
JSON_THROW_ON_ERRORKast JsonException i stedet for null
JSON_FORCE_OBJECTTving array til at blive object
JSON_NUMERIC_CHECKKonvertér numeriske strings til tal (vær varsom!)
JSON_PRESERVE_ZERO_FRACTIONBevar 1.0 som 1.0 (ikke 1)
JSON_PARTIAL_OUTPUT_ON_ERRORReturnér delvist output ved fejl

Best Practices

+Brug altid JSON_THROW_ON_ERROR - aldrig stol på null som fejlsignal
+Brug JSON_UNESCAPED_UNICODE i danske projekter - ellers bliver output uigenkendeligt
+Sæt Content-Type: application/json; charset=utf-8 på alle JSON-responses
+Læs raw input med file_get_contents("php://input"), ikke $_POST (som ikke parser JSON)
+Implementér JsonSerializable på value objects for at kontrollere hvad der eksponeres
+Validér JSON-struktur efter parsing - parsing succes betyder ikke at strukturen er korrekt
+Undgå JSON_NUMERIC_CHECK på user input - det kan ændre telefonnumre til floats
+Sæt en rimelig depth limit (3. argument) - default 512 er højt nok til de fleste cases