23: Working with JSON & APIs

Chapter 23: Working with JSON & APIs
Section titled “Chapter 23: Working with JSON & APIs”Overview
Section titled “Overview”Modern web applications rarely work in isolation. Whether you’re integrating with payment gateways, weather services, social media platforms, or building your own API, you need to exchange data with other systems. JSON (JavaScript Object Notation) has become the universal language for this data exchange.
In this chapter, you’ll learn how to encode and decode JSON, consume external APIs using cURL, handle errors gracefully, and build your own simple REST API endpoints. By the end, you’ll be able to integrate any web service into your PHP applications.
Prerequisites
Section titled “Prerequisites”- PHP 8.4 installed and accessible from your command line (Chapter 00)
- Understanding of arrays and associative arrays (Chapter 06)
- Familiarity with error handling (Chapter 11)
- Basic understanding of HTTP (GET, POST requests)
- A text editor
- Estimated time: 25 minutes
What You’ll Build
Section titled “What You’ll Build”By the end of this chapter, you will have created:
- JSON encoding and decoding scripts with error handling
- A cURL-based API client that fetches weather data
- A GitHub API integration that retrieves user information
- Your own simple REST API endpoint
- Error handling for API failures and JSON errors
- Working examples of all major JSON and API patterns
All examples will be working PHP scripts you can run immediately.
Quick Start
Section titled “Quick Start”Want to see JSON and API calls in action right away? Create this file and run it:
<?phpdeclare(strict_types=1);
// Encode PHP data to JSON$user = [ 'name' => 'Alice Johnson', 'email' => 'alice@example.com', 'roles' => ['admin', 'user']];
$json = json_encode($user, JSON_PRETTY_PRINT);echo "JSON Output:\n" . $json . PHP_EOL;
// Decode JSON back to PHP$decoded = json_decode($json, true);echo "\nDecoded Name: " . $decoded['name'] . PHP_EOL;
// Make an API request (using a free test API)$ch = curl_init('https://api.github.com/users/github');curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);curl_setopt($ch, CURLOPT_USERAGENT, 'PHP-Tutorial');$response = curl_exec($ch);curl_close($ch);
$data = json_decode($response, true);echo "\nGitHub User: " . $data['name'] . PHP_EOL;echo "Public Repos: " . $data['public_repos'] . PHP_EOL;# Run itphp quick-api.phpExpected output:
JSON Output:{ "name": "Alice Johnson", "email": "alice@example.com", "roles": [ "admin", "user" ]}
Decoded Name: Alice Johnson
GitHub User: GitHubPublic Repos: 296This compact example shows JSON encoding, decoding, and making an actual API call. Now let’s build this understanding step by step.
Objectives
Section titled “Objectives”- Understand JSON format and its relationship to PHP arrays
- Encode PHP data to JSON strings
- Decode JSON strings to PHP data structures
- Handle JSON encoding and decoding errors
- Make HTTP requests using cURL
- Consume REST APIs with proper error handling
- Build a simple REST API endpoint
- Work with common API authentication patterns
Step 1: JSON Basics (~5 min)
Section titled “Step 1: JSON Basics (~5 min)”Master JSON encoding and decoding with proper error handling.
Actions
Section titled “Actions”- Create a file named
json-basics.php:
<?php
declare(strict_types=1);
// Encode PHP array to JSON$products = [ [ 'id' => 1, 'name' => 'Laptop', 'price' => 999.99, 'in_stock' => true ], [ 'id' => 2, 'name' => 'Mouse', 'price' => 29.99, 'in_stock' => false ]];
// Basic encoding$json = json_encode($products);echo "Compact JSON:\n" . $json . "\n\n";
// Pretty-printed JSON (easier to read)$prettyJson = json_encode($products, JSON_PRETTY_PRINT);echo "Pretty JSON:\n" . $prettyJson . "\n\n";
// Decode JSON string back to PHP array$decoded = json_decode($prettyJson, true); // true = return associative arrayecho "First product name: " . $decoded[0]['name'] . PHP_EOL;
// Without true, you get an object$decodedAsObject = json_decode($prettyJson);echo "First product name (object): " . $decodedAsObject[0]->name . PHP_EOL;- Add error handling for encoding/decoding failures:
{# filename: json-error-handling.php #}<?php
declare(strict_types=1);
// Invalid UTF-8 sequences will cause encoding to fail$invalid = [ 'name' => "Invalid \xB1\x31 data"];
$json = json_encode($invalid);
if ($json === false) { echo "JSON encoding failed: " . json_last_error_msg() . PHP_EOL;} else { echo "Success: " . $json . PHP_EOL;}
// Decoding invalid JSON$badJson = '{"name": "Bob", "age": }'; // Missing value$decoded = json_decode($badJson, true);
if (json_last_error() !== JSON_ERROR_NONE) { echo "JSON decoding failed: " . json_last_error_msg() . PHP_EOL;}- Run each script to inspect the output:
php json-basics.phpphp json-error-handling.phpExpected Result
Section titled “Expected Result”Compact JSON:[{"id":1,"name":"Laptop","price":999.99,"in_stock":true},{"id":2,"name":"Mouse","price":29.99,"in_stock":false}]
Pretty JSON:[ { "id": 1, "name": "Laptop", "price": 999.99, "in_stock": true }, { "id": 2, "name": "Mouse", "price": 29.99, "in_stock": false }]
First product name: LaptopFirst product name (object): LaptopJSON encoding failed: Malformed UTF-8 characters, possibly incorrectly encodedJSON decoding failed: Syntax errorWhy It Works
Section titled “Why It Works”json_encode()converts PHP arrays/objects into JSON strings.json_decode()converts JSON strings back to PHP data structures; passingtruereturns associative arrays, whilefalsereturns objects.json_last_error_msg()reveals the reason for encoding/decoding failures, which is critical for debugging malformed or non-UTF-8 data.
Troubleshooting
Section titled “Troubleshooting”json_encodereturns false — Verify input strings are valid UTF-8 or enableJSON_PARTIAL_OUTPUT_ON_ERRORfor best-effort encoding.- Decoded data is
null— Checkjson_last_error()for decoding issues; ensure the JSON string is valid. - Large numbers lose precision — Use
JSON_BIGINT_AS_STRINGwhen decoding to keep big integers as strings.
JSON Error Handling
Section titled “JSON Error Handling”Always check for errors when working with JSON:
<?php
declare(strict_types=1);
// Invalid UTF-8 sequences will cause encoding to fail$invalid = [ 'name' => "Invalid \xB1\x31 data"];
$json = json_encode($invalid);
if ($json === false) { echo "JSON encoding failed: " . json_last_error_msg() . PHP_EOL;} else { echo "Success: " . $json . PHP_EOL;}
// Decoding invalid JSON$badJson = '{"name": "Bob", "age": }'; // Missing value$decoded = json_decode($badJson, true);
if (json_last_error() !== JSON_ERROR_NONE) { echo "JSON decoding failed: " . json_last_error_msg() . PHP_EOL;}::: tip JSON Options
Common json_encode() options:
JSON_PRETTY_PRINT- Format with indentationJSON_UNESCAPED_UNICODE- Don’t escape unicode charactersJSON_UNESCAPED_SLASHES- Don’t escape forward slashesJSON_THROW_ON_ERROR- Throw exceptions instead of returning false (PHP 7.3+) :::
Step 2: Making API Requests with cURL (~8 min)
Section titled “Step 2: Making API Requests with cURL (~8 min)”Use cURL to consume external REST APIs with proper error handling.
Actions
Section titled “Actions”- Create a file named
api-client-curl.php:
<?php
declare(strict_types=1);
/** * Fetch user data from GitHub API */function fetchGitHubUser(string $username): ?array{ $url = "https://api.github.com/users/" . urlencode($username);
// Initialize cURL session $ch = curl_init($url);
// Set options curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); // Return response as string curl_setopt($ch, CURLOPT_USERAGENT, 'PHP-Tutorial'); // GitHub requires user agent curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); // Follow redirects curl_setopt($ch, CURLOPT_TIMEOUT, 10); // 10 second timeout
// Execute request $response = curl_exec($ch);
// Check for cURL errors if ($response === false) { echo "cURL Error: " . curl_error($ch) . PHP_EOL; curl_close($ch); return null; }
// Get HTTP status code $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch);
if ($httpCode !== 200) { echo "HTTP Error: " . $httpCode . PHP_EOL; return null; }
$data = json_decode($response, true);
if (json_last_error() !== JSON_ERROR_NONE) { echo "JSON Decode Error: " . json_last_error_msg() . PHP_EOL; return null; }
return $data;}
$user = fetchGitHubUser('github');
if ($user) { echo "Name: " . $user['name'] . PHP_EOL; echo "Public Repos: " . $user['public_repos'] . PHP_EOL;}- Run the script:
php api-client-curl.phpExpected Result
Section titled “Expected Result”Name: GitHubPublic Repos: 296Why It Works
Section titled “Why It Works”curl_setoptflags configure cURL to return data, follow redirects, and set timeouts.- Handling HTTP status codes ensures success responses before decoding JSON.
- Decoding the JSON response yields an associative array for easy access to fields like
nameandpublic_repos.
Troubleshooting
Section titled “Troubleshooting”cURL Error: Could not resolve host— Check your internet connection or DNS settings.- HTTP Error responses (401, 403) — API may require authentication or a valid User-Agent.
- JSON decode errors — Log the raw response to inspect API output before decoding.
Step 3: Building a REST API Endpoint (~7 min)
Section titled “Step 3: Building a REST API Endpoint (~7 min)”Create a simple REST endpoint that returns JSON using plain PHP.
Actions
Section titled “Actions”- Create an API endpoint
public/api/posts.php:
<?php
declare(strict_types=1);
header('Content-Type: application/json');
$posts = [ ['id' => 1, 'title' => 'Hello JSON', 'content' => 'JSON is great for APIs'], ['id' => 2, 'title' => 'Consuming APIs', 'content' => 'cURL makes it easy'],];
if ($_SERVER['REQUEST_METHOD'] === 'GET') { echo json_encode($posts, JSON_PRETTY_PRINT); exit;}
echo json_encode(['error' => 'Method not allowed'], JSON_PRETTY_PRINT);http_response_code(405);- Serve the endpoint and test it:
php -S localhost:8000 -t publiccurl http://localhost:8000/api/posts.phpExpected Result
Section titled “Expected Result”[ { "id": 1, "title": "Hello JSON", "content": "JSON is great for APIs" }, { "id": 2, "title": "Consuming APIs", "content": "cURL makes it easy" }]Why It Works
Section titled “Why It Works”- Setting the
Content-Typeheader ensures clients treat the response as JSON. json_encodeconverts the posts array into a properly formatted string.- Returning a 405 status code for unsupported methods follows REST best practices.
Troubleshooting
Section titled “Troubleshooting”- Blank output — Ensure
json_encodeis called and no syntax errors occurred before the response. Cannot redeclare header()warnings — Make sure no output is sent before theheader()call.curlshows HTML instead of JSON — Confirm you’re hitting the correct endpoint and not the default router.
Step 4: Adding Authentication (~6 min)
Section titled “Step 4: Adding Authentication (~6 min)”Demonstrate a simple token-based authentication check for API requests.
Actions
Section titled “Actions”- Update your endpoint to require an API token:
<?php
declare(strict_types=1);
header('Content-Type: application/json');
$token = $_GET['token'] ?? '';
if ($token !== 'secret123') { http_response_code(401); echo json_encode(['error' => 'Unauthorized']); exit;}
$posts = [ ['id' => 1, 'title' => 'Hello JSON', 'content' => 'JSON is great for APIs'], ['id' => 2, 'title' => 'Consuming APIs', 'content' => 'cURL makes it easy'],];
echo json_encode($posts, JSON_PRETTY_PRINT);- Call the endpoint with the token:
curl "http://localhost:8000/api/posts.php?token=secret123"Expected Result
Section titled “Expected Result”Requests without the token return a 401 Unauthorized response with an error message. Requests with the correct token return the JSON payload.
Why It Works
Section titled “Why It Works”- A simple token check enforces authentication before serving data.
- Returning 401 status codes communicates auth errors clearly to clients.
Troubleshooting
Section titled “Troubleshooting”- Always unauthorized — Confirm the query string includes
token=secret123and matches exactly, case-sensitive. - Token visible in URL — For sensitive data, use headers (
Authorization: Bearer) instead of query parameters.
Step 5: Handling JSON Errors (~4 min)
Section titled “Step 5: Handling JSON Errors (~4 min)”Gracefully handle encoding/decoding errors and provide actionable feedback.
Actions
Section titled “Actions”- Create a utility function for safe decoding:
<?php
declare(strict_types=1);
function decodeJson(string $json): array{ $data = json_decode($json, true);
if (json_last_error() !== JSON_ERROR_NONE) { throw new RuntimeException('JSON decode failed: ' . json_last_error_msg()); }
return $data;}
try { $payload = decodeJson('{"foo": "bar"}'); print_r($payload);
$broken = decodeJson('{"foo": }');} catch (RuntimeException $e) { echo $e->getMessage() . PHP_EOL;}- Run the helper:
php json-safe-decode.phpExpected Result
Section titled “Expected Result”Array( [foo] => bar)JSON decode failed: Syntax errorWhy It Works
Section titled “Why It Works”- Wrapping
json_decodein a helper centralizes error handling and keeps calling code clean. - Throwing exceptions clarifies which part of the pipeline failed, aiding debugging.
Troubleshooting
Section titled “Troubleshooting”- Helper throws exceptions on valid JSON — Ensure the input is a UTF-8 string and
json_last_error()is checked immediately after decoding. - Need partial results — Use
JSON_PARTIAL_OUTPUT_ON_ERRORand handle truncated data carefully.
Wrap-up
Section titled “Wrap-up”Great job! You’ve now combined JSON handling, API consumption, and simple REST design into a clean workflow. You can:
- ✅ Encode PHP arrays and objects into JSON with proper error handling
- ✅ Decode JSON safely and detect malformed payloads
- ✅ Fetch remote API data using cURL with timeouts and error checks
- ✅ Build basic REST endpoints that serve JSON responses
- ✅ Add authentication gates to protect API endpoints
- ✅ Centralize JSON validation logic for reuse across projects
These skills unlock third-party integrations, microservices, and modern front-end frameworks that expect JSON APIs.
Further Reading
Section titled “Further Reading”- PHP Manual: JSON Functions
- cURL Manual — Detailed cURL command reference
- HTTP Status Codes — Official MDN guide
- REST API Design Guidelines — Best practices for designing RESTful services
Knowledge Check
Section titled “Knowledge Check”Test your understanding of JSON and APIs:
Further Reading
Section titled “Further Reading”- PHP JSON Functions Documentation
- PHP cURL Documentation
- REST API Tutorial
- HTTP Status Codes Reference
- JSONPlaceholder - Free fake API for testing