Session 4.4 – Working with JSON in PHP

Module 4: PHP Filters and Handlers | Duration: 1 hr

Learning Objectives

By the end of this session, students will be able to:

  • Understand JSON format and its importance in web development
  • Encode PHP data structures to JSON using json_encode()
  • Decode JSON strings to PHP data structures using json_decode()
  • Use various JSON encoding and decoding options
  • Handle JSON errors and implement proper error checking
  • Work with RESTful APIs and consume JSON data
  • Build API endpoints that return JSON responses

Introduction to JSON in PHP

JSON (JavaScript Object Notation) is a lightweight data interchange format that is easy for humans to read and write, and easy for machines to parse and generate. It has become the de facto standard for data exchange in modern web applications and APIs.

Key Insight

JSON provides a simple, text-based format for representing structured data. PHP's built-in JSON functions make it easy to convert between PHP data structures and JSON, enabling seamless data exchange between PHP applications and other systems.

What is JSON?

JSON is built on two structures:

Objects

A collection of key-value pairs enclosed in curly braces {}

{"name": "John", "age": 30}
Arrays

An ordered list of values enclosed in square brackets []

["apple", "banana", "cherry"]
JSON Data Types:
{
    "string": "Hello World",
    "number": 42,
    "float": 3.14,
    "boolean": true,
    "null": null,
    "array": [1, 2, 3],
    "object": {
        "nested": "value"
    }
}
JSON vs PHP Arrays

While JSON looks similar to PHP arrays, there are important differences:

  • JSON keys must be strings and must be in double quotes
  • JSON uses true, false, and null (lowercase)
  • JSON doesn't support PHP-specific types like resources or objects
  • JSON arrays are always zero-indexed with consecutive keys

Encoding JSON with json_encode()

The json_encode() function converts PHP values to JSON format.

Basic Encoding:
<?php
// Encode array to JSON
$fruits = ["apple", "banana", "cherry"];
$json = json_encode($fruits);
echo $json; // Output: ["apple","banana","cherry"]

// Encode associative array to JSON
$person = [
    "name" => "John Doe",
    "age" => 30,
    "email" => "john@example.com"
];
$json = json_encode($person);
echo $json;
// Output: {"name":"John Doe","age":30,"email":"john@example.com"}
?>
Encoding Complex Data Structures:
<?php
$data = [
    "user" => [
        "id" => 123,
        "name" => "John Doe",
        "email" => "john@example.com",
        "roles" => ["admin", "editor"],
        "active" => true,
        "profile" => [
            "bio" => "Software Developer",
            "location" => "New York",
            "website" => null
        ]
    ],
    "timestamp" => time(),
    "version" => "1.0"
];

$json = json_encode($data);
echo $json;
?>
Encoding Objects:
<?php
class User {
    public $id;
    public $name;
    private $password; // Private properties are excluded

    public function __construct($id, $name, $password) {
        $this->id = $id;
        $this->name = $name;
        $this->password = $password;
    }
}

$user = new User(1, "Alice", "secret123");
$json = json_encode($user);
echo $json; // Output: {"id":1,"name":"Alice"}
// Note: private $password is not included
?>
Pretty Printing JSON:
<?php
$data = [
    "name" => "John",
    "age" => 30,
    "hobbies" => ["reading", "gaming"]
];

// Without pretty print
echo json_encode($data);
// {"name":"John","age":30,"hobbies":["reading","gaming"]}

// With pretty print
echo json_encode($data, JSON_PRETTY_PRINT);
/*
{
    "name": "John",
    "age": 30,
    "hobbies": [
        "reading",
        "gaming"
    ]
}
*/
?>

Decoding JSON with json_decode()

The json_decode() function parses a JSON string and converts it into a PHP variable.

Basic Decoding:
<?php
$json = '{"name":"John","age":30,"city":"New York"}';

// Decode as object (default)
$obj = json_decode($json);
echo $obj->name;  // Output: John
echo $obj->age;   // Output: 30

// Decode as associative array
$arr = json_decode($json, true);
echo $arr["name"]; // Output: John
echo $arr["age"];  // Output: 30
?>
Decoding Arrays:
<?php
$json = '["apple","banana","cherry"]';

// Decode array
$fruits = json_decode($json);
print_r($fruits); // Array ( [0] => apple [1] => banana [2] => cherry )

foreach ($fruits as $fruit) {
    echo $fruit . "\n";
}
?>
Decoding Nested Structures:
<?php
$json = '{
    "user": {
        "id": 123,
        "name": "John Doe",
        "contacts": {
            "email": "john@example.com",
            "phone": "555-1234"
        },
        "roles": ["admin", "editor"]
    }
}';

// Decode as object
$data = json_decode($json);
echo $data->user->name; // Output: John Doe
echo $data->user->contacts->email; // Output: john@example.com
print_r($data->user->roles); // Array ( [0] => admin [1] => editor )

// Decode as array
$data = json_decode($json, true);
echo $data["user"]["name"]; // Output: John Doe
echo $data["user"]["contacts"]["email"]; // Output: john@example.com
?>
Decoding with Depth Limit:
<?php
$json = '{"a":{"b":{"c":{"d":"value"}}}}';

// Set maximum depth to 3
$data = json_decode($json, true, 3);

// This will fail because depth exceeds 3
if ($data === null) {
    echo "Depth limit exceeded";
}
?>

JSON Encoding and Decoding Options

PHP provides various options to customize JSON encoding and decoding behavior.

Common json_encode() Options:
<?php
$data = [
    "name" => "John & Jane",
    "url" => "https://example.com/path?query=value",
    "unicode" => "Héllo Wörld",
    "number" => 1234567890123456789
];

// JSON_HEX_TAG - Escape < and >
echo json_encode($data, JSON_HEX_TAG) . "\n";

// JSON_HEX_AMP - Escape &
echo json_encode($data, JSON_HEX_AMP) . "\n";

// JSON_HEX_QUOT - Escape "
echo json_encode($data, JSON_HEX_QUOT) . "\n";

// JSON_UNESCAPED_SLASHES - Don't escape /
echo json_encode($data, JSON_UNESCAPED_SLASHES) . "\n";

// JSON_UNESCAPED_UNICODE - Don't escape Unicode characters
echo json_encode($data, JSON_UNESCAPED_UNICODE) . "\n";

// JSON_NUMERIC_CHECK - Encode strings that look like numbers as numbers
$mixed = ["age" => "30", "score" => "98.5", "name" => "John"];
echo json_encode($mixed, JSON_NUMERIC_CHECK);
// Output: {"age":30,"score":98.5,"name":"John"}

// JSON_FORCE_OBJECT - Force array to be encoded as object
$array = ["a", "b", "c"];
echo json_encode($array, JSON_FORCE_OBJECT);
// Output: {"0":"a","1":"b","2":"c"}

// Combine multiple options with | (bitwise OR)
echo json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
?>
Common json_decode() Options:
<?php
$json = '{"large_number": 12345678901234567890}';

// JSON_BIGINT_AS_STRING - Decode large integers as strings
$data = json_decode($json, true, 512, JSON_BIGINT_AS_STRING);
echo $data["large_number"]; // String: "12345678901234567890"

// JSON_OBJECT_AS_ARRAY - Decode JSON objects as arrays
$json = '{"key": "value"}';
$data = json_decode($json, false, 512, JSON_OBJECT_AS_ARRAY);
// Returns array instead of object
?>

JSON Error Handling

Always check for errors when working with JSON to handle malformed data gracefully.

Checking for Errors:
<?php
// json_last_error() returns the last error code
// json_last_error_msg() returns the error message

$json = '{"name": "John", "age": }'; // Invalid JSON (missing value)

$data = json_decode($json);

if (json_last_error() !== JSON_ERROR_NONE) {
    echo "JSON Error: " . json_last_error_msg();
    // Output: JSON Error: Syntax error
}

// Check specific error codes
switch (json_last_error()) {
    case JSON_ERROR_NONE:
        echo "No errors";
        break;
    case JSON_ERROR_DEPTH:
        echo "Maximum stack depth exceeded";
        break;
    case JSON_ERROR_STATE_MISMATCH:
        echo "Invalid or malformed JSON";
        break;
    case JSON_ERROR_CTRL_CHAR:
        echo "Control character error";
        break;
    case JSON_ERROR_SYNTAX:
        echo "Syntax error, malformed JSON";
        break;
    case JSON_ERROR_UTF8:
        echo "Malformed UTF-8 characters";
        break;
    default:
        echo "Unknown JSON error";
        break;
}
?>
Safe JSON Encoding/Decoding Function:
<?php
function safeJsonEncode($data, $options = 0) {
    $json = json_encode($data, $options);

    if ($json === false) {
        throw new Exception('JSON encoding failed: ' . json_last_error_msg());
    }

    return $json;
}

function safeJsonDecode($json, $assoc = false) {
    $data = json_decode($json, $assoc);

    if (json_last_error() !== JSON_ERROR_NONE) {
        throw new Exception('JSON decoding failed: ' . json_last_error_msg());
    }

    return $data;
}

// Usage
try {
    $data = ["name" => "John", "age" => 30];
    $json = safeJsonEncode($data);
    echo $json;

    $decoded = safeJsonDecode($json, true);
    print_r($decoded);
} catch (Exception $e) {
    echo "Error: " . $e->getMessage();
}
?>
JSON Validation Function:
<?php
function isValidJson($string) {
    json_decode($string);
    return json_last_error() === JSON_ERROR_NONE;
}

// Usage
$valid = '{"name": "John"}';
$invalid = '{name: "John"}'; // Missing quotes on key

echo isValidJson($valid) ? "Valid" : "Invalid"; // Output: Valid
echo isValidJson($invalid) ? "Valid" : "Invalid"; // Output: Invalid
?>

Working with APIs and JSON

JSON is the standard format for RESTful APIs. Learn how to consume and create JSON APIs.

Consuming a JSON API:
<?php
// Fetch data from an API
function fetchJsonApi($url) {
    $ch = curl_init();

    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);

    $response = curl_exec($ch);
    $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);

    curl_close($ch);

    if ($httpCode !== 200) {
        throw new Exception("API request failed with HTTP code: " . $httpCode);
    }

    $data = json_decode($response, true);

    if (json_last_error() !== JSON_ERROR_NONE) {
        throw new Exception("JSON decode error: " . json_last_error_msg());
    }

    return $data;
}

// Usage
try {
    $data = fetchJsonApi('https://api.example.com/users/1');
    echo "User: " . $data['name'];
} catch (Exception $e) {
    echo "Error: " . $e->getMessage();
}
?>
Creating a JSON API Endpoint:
<?php
// api.php - Simple JSON API endpoint

// Set JSON content type header
header('Content-Type: application/json');

// Get request method
$method = $_SERVER['REQUEST_METHOD'];

// Response data
$response = [];

switch ($method) {
    case 'GET':
        // Get resource
        if (isset($_GET['id'])) {
            $response = [
                'success' => true,
                'data' => [
                    'id' => $_GET['id'],
                    'name' => 'John Doe',
                    'email' => 'john@example.com'
                ]
            ];
        } else {
            $response = [
                'success' => true,
                'data' => [
                    ['id' => 1, 'name' => 'John Doe'],
                    ['id' => 2, 'name' => 'Jane Smith']
                ]
            ];
        }
        break;

    case 'POST':
        // Create resource
        $input = json_decode(file_get_contents('php://input'), true);

        if ($input) {
            $response = [
                'success' => true,
                'message' => 'Resource created',
                'data' => $input
            ];
            http_response_code(201); // Created
        } else {
            $response = [
                'success' => false,
                'error' => 'Invalid JSON input'
            ];
            http_response_code(400); // Bad Request
        }
        break;

    case 'PUT':
        // Update resource
        $input = json_decode(file_get_contents('php://input'), true);
        $response = [
            'success' => true,
            'message' => 'Resource updated',
            'data' => $input
        ];
        break;

    case 'DELETE':
        // Delete resource
        $response = [
            'success' => true,
            'message' => 'Resource deleted'
        ];
        break;

    default:
        $response = [
            'success' => false,
            'error' => 'Method not allowed'
        ];
        http_response_code(405); // Method Not Allowed
}

// Output JSON response
echo json_encode($response, JSON_PRETTY_PRINT);
?>
RESTful API Class:
<?php
class JsonApiResponse {
    private $statusCode = 200;
    private $data = [];

    public function success($data, $message = null) {
        $this->data = [
            'success' => true,
            'data' => $data
        ];

        if ($message) {
            $this->data['message'] = $message;
        }

        return $this;
    }

    public function error($message, $code = 400) {
        $this->statusCode = $code;
        $this->data = [
            'success' => false,
            'error' => $message
        ];

        return $this;
    }

    public function send() {
        http_response_code($this->statusCode);
        header('Content-Type: application/json');
        echo json_encode($this->data, JSON_PRETTY_PRINT);
        exit;
    }
}

// Usage
$api = new JsonApiResponse();

// Success response
$api->success(['id' => 1, 'name' => 'John'], 'User retrieved successfully')->send();

// Error response
$api->error('User not found', 404)->send();
?>

Practical Examples

Example 1: JSON Configuration File Handler
<?php
class ConfigManager {
    private $configFile;
    private $config = [];

    public function __construct($file) {
        $this->configFile = $file;
        $this->load();
    }

    private function load() {
        if (file_exists($this->configFile)) {
            $json = file_get_contents($this->configFile);
            $this->config = json_decode($json, true);

            if (json_last_error() !== JSON_ERROR_NONE) {
                throw new Exception("Invalid config file: " . json_last_error_msg());
            }
        }
    }

    public function get($key, $default = null) {
        return $this->config[$key] ?? $default;
    }

    public function set($key, $value) {
        $this->config[$key] = $value;
    }

    public function save() {
        $json = json_encode($this->config, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);

        if (file_put_contents($this->configFile, $json) === false) {
            throw new Exception("Failed to save config file");
        }
    }

    public function all() {
        return $this->config;
    }
}

// Usage
$config = new ConfigManager('config.json');

// Get values
echo $config->get('database.host', 'localhost');
echo $config->get('app.name', 'My App');

// Set values
$config->set('database.host', '127.0.0.1');
$config->set('app.debug', true);

// Save changes
$config->save();
?>
Example 2: JSON Data Export/Import
<?php
class DataExporter {
    public static function exportToJson($data, $filename) {
        $json = json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);

        if ($json === false) {
            throw new Exception("JSON encoding failed");
        }

        // Set headers for download
        header('Content-Type: application/json');
        header('Content-Disposition: attachment; filename="' . $filename . '"');
        header('Content-Length: ' . strlen($json));

        echo $json;
        exit;
    }

    public static function importFromJson($file) {
        if (!file_exists($file)) {
            throw new Exception("File not found");
        }

        $json = file_get_contents($file);
        $data = json_decode($json, true);

        if (json_last_error() !== JSON_ERROR_NONE) {
            throw new Exception("Invalid JSON: " . json_last_error_msg());
        }

        return $data;
    }
}

// Export data
$users = [
    ['id' => 1, 'name' => 'John', 'email' => 'john@example.com'],
    ['id' => 2, 'name' => 'Jane', 'email' => 'jane@example.com']
];

// DataExporter::exportToJson($users, 'users.json');

// Import data
try {
    $imported = DataExporter::importFromJson('users.json');
    print_r($imported);
} catch (Exception $e) {
    echo "Import error: " . $e->getMessage();
}
?>
Example 3: AJAX Handler
<?php
// ajax_handler.php

// Only accept AJAX requests
if (!isset($_SERVER['HTTP_X_REQUESTED_WITH']) ||
    strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) !== 'xmlhttprequest') {
    http_response_code(403);
    die('Direct access not allowed');
}

header('Content-Type: application/json');

$action = $_POST['action'] ?? '';

switch ($action) {
    case 'get_users':
        $users = [
            ['id' => 1, 'name' => 'John Doe', 'status' => 'active'],
            ['id' => 2, 'name' => 'Jane Smith', 'status' => 'inactive']
        ];

        echo json_encode([
            'success' => true,
            'data' => $users
        ]);
        break;

    case 'save_user':
        $name = $_POST['name'] ?? '';
        $email = $_POST['email'] ?? '';

        if (empty($name) || empty($email)) {
            echo json_encode([
                'success' => false,
                'error' => 'Name and email are required'
            ]);
        } else {
            // Save user (simulated)
            echo json_encode([
                'success' => true,
                'message' => 'User saved successfully',
                'data' => ['id' => 123, 'name' => $name, 'email' => $email]
            ]);
        }
        break;

    default:
        echo json_encode([
            'success' => false,
            'error' => 'Invalid action'
        ]);
}
?>

<!-- HTML/JavaScript Example -->
<script>
// Fetch users
fetch('ajax_handler.php', {
    method: 'POST',
    headers: {
        'Content-Type': 'application/x-www-form-urlencoded',
        'X-Requested-With': 'XMLHttpRequest'
    },
    body: 'action=get_users'
})
.then(response => response.json())
.then(data => {
    if (data.success) {
        console.log('Users:', data.data);
    }
});

// Save user
fetch('ajax_handler.php', {
    method: 'POST',
    headers: {
        'Content-Type': 'application/x-www-form-urlencoded',
        'X-Requested-With': 'XMLHttpRequest'
    },
    body: 'action=save_user&name=John&email=john@example.com'
})
.then(response => response.json())
.then(data => {
    if (data.success) {
        console.log('User saved:', data.data);
    }
});
</script>
Example 4: JSON Database (Simple File-based DB)
<?php
class JsonDatabase {
    private $file;
    private $data = [];

    public function __construct($file) {
        $this->file = $file;
        $this->load();
    }

    private function load() {
        if (file_exists($this->file)) {
            $json = file_get_contents($this->file);
            $this->data = json_decode($json, true) ?? [];
        }
    }

    private function save() {
        $json = json_encode($this->data, JSON_PRETTY_PRINT);
        file_put_contents($this->file, $json);
    }

    public function insert($table, $record) {
        if (!isset($this->data[$table])) {
            $this->data[$table] = [];
        }

        $record['id'] = $this->getNextId($table);
        $this->data[$table][] = $record;
        $this->save();

        return $record['id'];
    }

    public function find($table, $id) {
        if (!isset($this->data[$table])) {
            return null;
        }

        foreach ($this->data[$table] as $record) {
            if ($record['id'] == $id) {
                return $record;
            }
        }

        return null;
    }

    public function all($table) {
        return $this->data[$table] ?? [];
    }

    public function update($table, $id, $updates) {
        if (!isset($this->data[$table])) {
            return false;
        }

        foreach ($this->data[$table] as &$record) {
            if ($record['id'] == $id) {
                $record = array_merge($record, $updates);
                $this->save();
                return true;
            }
        }

        return false;
    }

    public function delete($table, $id) {
        if (!isset($this->data[$table])) {
            return false;
        }

        $this->data[$table] = array_filter($this->data[$table], function($record) use ($id) {
            return $record['id'] != $id;
        });

        $this->data[$table] = array_values($this->data[$table]);
        $this->save();

        return true;
    }

    private function getNextId($table) {
        if (!isset($this->data[$table]) || empty($this->data[$table])) {
            return 1;
        }

        $ids = array_column($this->data[$table], 'id');
        return max($ids) + 1;
    }
}

// Usage
$db = new JsonDatabase('database.json');

// Insert
$userId = $db->insert('users', [
    'name' => 'John Doe',
    'email' => 'john@example.com'
]);

// Find
$user = $db->find('users', $userId);
print_r($user);

// Update
$db->update('users', $userId, ['email' => 'newemail@example.com']);

// Get all
$allUsers = $db->all('users');
print_r($allUsers);

// Delete
$db->delete('users', $userId);
?>

Session Summary

Key Points
  • JSON is a lightweight, text-based data interchange format
  • Use json_encode() to convert PHP data to JSON format
  • Use json_decode() to parse JSON strings into PHP variables
  • Always check for errors using json_last_error() and json_last_error_msg()
  • Use encoding options like JSON_PRETTY_PRINT, JSON_UNESCAPED_SLASHES for better formatting
  • JSON is the standard format for RESTful APIs and AJAX communication
  • Set proper headers (Content-Type: application/json) when creating JSON APIs
  • JSON can be used for configuration files, data export/import, and simple databases
Next Session Preview

In the next session, we will explore PHP Filters, learning how to validate and sanitize user input to prevent security vulnerabilities and ensure data integrity.