ZetCode

PHP JSON

last modified January 10, 2023

PHP JSON tutorial shows how to work with JSON in PHP.

$ php -v
php -v
PHP 8.1.2 (cli) (built: Aug  8 2022 07:28:23) (NTS)
...

We use PHP version 8.1.2.

JSON

JSON (JavaScript Object Notation) is a lightweight data-interchange format. It is easily read and written by humans and parsed and generated by machines. The application/json is the official Internet media type for JSON. The JSON filename extension is .json.

The json_encode function returns the JSON representation of the given value. The json_decode takes a JSON encoded string and converts it into a PHP variable.

PHP frameworks such as Symfony and Laravel have built-in methods that work with JSON.

PHP JSON encode

In the following example, we use the json_encode function.

encode.php
<?php

$data = ["falcon", "sky", "cloud", "orange", "wood", "forest"];

header('Content-type:application/json;charset=utf-8');
echo json_encode($data);

The example transforms a PHP array into a JSON string.

$ php -S localhost:8000 encode.php

We start the server and locate to the localhost:8000.

JSON view in Firefox
Figure: JSON view in Firefox

Modern web browsers show JSON view for JSON data when they receive an appropriate content type in the header of the response.

PHP JSON decode

In the following example, we use the json_decode function.

decode.php
<?php

$data = '{
    "name": "John Doe",
    "occupation": "gardener"
}';

$a = json_decode($data, true);

header('Content-type:text/html;charset=utf-8');
echo "{$a["name"]} is a {$a["occupation"]}";

The example transforms a JSON string into a PHP variable.

$ php -S localhost:8000 decode.php

We start the server.

$ curl localhost:8000
John Doe is a gardener

We send a GET request with curl.

PHP JSON read from file

In the following example, we read JSON data from a file.

data.json
[
    {"name": "John Doe", "occupation": "gardener", "country": "USA"},
    {"name": "Richard Roe", "occupation": "driver", "country": "UK"},
    {"name": "Sibel Schumacher", "occupation": "architect", "country": "Germany"},
    {"name": "Manuella Navarro", "occupation": "teacher", "country": "Spain"},
    {"name": "Thomas Dawn", "occupation": "teacher", "country": "New Zealand"},
    {"name": "Morris Holmes", "occupation": "programmer", "country": "Ireland"}
]

This is the JSON data.

readjson.php
<?php

$filename = 'data.json';

$data = file_get_contents($filename);
$users = json_decode($data);
?>

<html>
<table>
    <tbody>
        <tr>
            <th>Name</th>
            <th>Occupation</th>
            <th>Country</th>
        </tr>
        <?php foreach ($users as $user) { ?>
        <tr>
            <td> <?= $user->name; ?> </td>
            <td> <?= $user->occupation; ?> </td>
            <td> <?= $user->country; ?> </td>
        </tr>
        <?php } ?>
    </tbody>
</table>
</html>

In the code example, we read the file with file_get_contents and decode it into an PHP array with json_decode. Later, we place the data into a table utilizing PHP foreach loop.

PHP JSON read from database

In the following example, we read data from an SQLite database and return it in JSON.

cities.sql
BEGIN TRANSACTION;
DROP TABLE IF EXISTS cities;

CREATE TABLE cities(id INTEGER PRIMARY KEY, name TEXT, population INTEGER);
INSERT INTO cities(name, population) VALUES('Bratislava', 432000);
INSERT INTO cities(name, population) VALUES('Budapest', 1759000);
INSERT INTO cities(name, population) VALUES('Prague', 1280000);
INSERT INTO cities(name, population) VALUES('Warsaw', 1748000);
INSERT INTO cities(name, population) VALUES('Los Angeles', 3971000);
INSERT INTO cities(name, population) VALUES('New York', 8550000);
INSERT INTO cities(name, population) VALUES('Edinburgh', 464000);
INSERT INTO cities(name, population) VALUES('Berlin', 3671000);
COMMIT;

This SQL code creates a cities table in SQLite.

$ sqlite3 test.db
sqlite> .read cities.sql
sqlite> SELECT * FROM cities;
1|Bratislava|432000
2|Budapest|1759000
3|Prague|1280000
4|Warsaw|1748000
5|Los Angeles|3971000
6|New York|8550000
7|Edinburgh|464000
8|Berlin|3671000

With the sqlite3 command line tool, we generate an SQLite database and create the cities table.

fetch_all.php
<?php

$db = new SQLite3('test.db');
$res = $db->query('SELECT * FROM cities');
$cities = [];

while ($row = $res->fetchArray()) {
    $cities[] = $row;
}

header('Content-type:application/json;charset=utf-8');
echo json_encode(['cities' => $cities]);

In the example, we retrieve the data from the database and return it as JSON.

PHP JSON and JS fetch API

In the following example, we use JavaScript fetch API to get the JSON data from a PHP script.

data.json
[
    {"name": "John Doe", "occupation": "gardener", "country": "USA"},
    {"name": "Richard Roe", "occupation": "driver", "country": "UK"},
    {"name": "Sibel Schumacher", "occupation": "architect", "country": "Germany"},
    {"name": "Manuella Navarro", "occupation": "teacher", "country": "Spain"},
    {"name": "Thomas Dawn", "occupation": "teacher", "country": "New Zealand"},
    {"name": "Morris Holmes", "occupation": "programmer", "country": "Ireland"}
]

The JSON data is stored in a file.

data.php
<?php

$filename = 'data.json';

$data = file_get_contents($filename);
header('Content-type:application/json;charset=utf-8');
echo $data;

We read the data and return it to the client.

index.html
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Home page</title>
    <style>
        th,
        td {
            font: 15px 'Segoe UI';
        }

        table,
        th,
        td {
            border: solid 1px #ddd;
            border-collapse: collapse;
            padding: 2px 3px;
            text-align: center;
        }
        tr:nth-child(odd) {background: #efefef}
        th {
            font-weight: bold;
        }
    </style>
</head>

<body>

    <button id="getData">Get data</button>
    <br>
    <br>
    <div id="output"></div>


    <script>

        const getBtn = document.getElementById('getData');
        const output = document.getElementById('output');
        const table = document.getElementById('table');

        getBtn.addEventListener('click', () => {

            fetch('/data.php')
                .then((res) => {
                    return res.json();
                })
                .then((data) => {
                    output.innerHTML = '';
                    let table = createTableAndHeader();
                    
                    output.appendChild(table);
                    appendRows(table, data);
                })
                .catch((err) => {
                    console.log("error fetching data");
                    console.log(err);
                })

        });

        function createTableAndHeader() {

            let table = document.createElement("table");

            let tr = table.insertRow(-1);
            let headers = ["Name", "Occupation", "Country"];

            for (let i = 0; i < 3; i++) {

                let th = document.createElement("th");
                th.innerHTML = headers[i];
                tr.appendChild(th);
            }

            return table;
        }

        function appendRows(table, data) {

            for (let i = 0; i < data.length; i++) {

                let tr = table.insertRow(-1);

                for (const [_, value] of Object.entries(data[i])) {

                    let cell = tr.insertCell(-1);
                    cell.innerHTML = value;
                }
            }
        }
    </script>
</body>

</html>

We have a button in the document. When we click on the button, the fetch function retrieves JSON data from the data.php script. An HTML table is dynamically built and filled with the data.

HTML table filled with JSON data from fetch API
Figure: HTML table filled with JSON data from fetch API

PHP JSON in Slim

In the following example, we return JSON data from a Slim application.

$ composer req slim/slim
$ composer req slim/psr7
$ composer req slim/http

We install slim/slim, slim/psr7, and slim/http packages.

public/index.php
<?php

use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use Slim\Factory\AppFactory;

require __DIR__ . '/../vendor/autoload.php';

$app = AppFactory::create();

$app->get('/city', function (Request $request, Response $response): Response {

    $cities = [
        ["id" => 1, "name" => "Bratislava", "population" => 432000],
        ["id" => 2, "name" => "Budapest", "population" => 1759000],
        ["id" => 3, "name" => "Prague", "population" => 1280000],
        ["id" => 4, "name" => "Warsaw", "population" => 1748000],
        ["id" => 5, "name" => "Los Angeles", "population" => 3971000],
        ["id" => 6, "name" => "New York", "population" => 8550000],
        ["id" => 7, "name" => "Edinburgh", "population" => 464000],
        ["id" => 8, "name" => "Berlin", "population" => 3671000],
    ];

    return $response->withJson(["cities" => $cities]);
});

$app->run();

We use the withJson method of the Response to return JSON data.

PHP JSON in Symfony

In the following example, we send a JSON response from a Symfony application.

$ symfony new symjson
$ cd symjson

A new application is created.

$ composer req annot
$ composer req symfony/orm-pack
$ composer req symfony/console

We install the annot, symfony/orm-pack, and symfony/console components.

.env
...
DATABASE_URL=sqlite:///%kernel.project_dir%/var/ydb.db
...

We set up a database URL for the SQLite database.

$ php bin/console doctrine:database:create

We create the database.

src/Command/CreateCityTable.php
<?php

namespace App\Command;

use Doctrine\DBAL\Driver\Connection;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;


class CreateCityTable extends Command
{
    private $conn;

    public function __construct(Connection $conn)
    {
        $this->conn = $conn;

        parent::__construct();
    }

    protected function configure()
    {
        $this->setName('app:create-table')
            ->setDescription('Creates City table')
            ->setHelp('This command creates a City table');
    }

    protected function execute(InputInterface $input, OutputInterface $output): int
    {
        $q1 = "CREATE TABLE cities(id INTEGER PRIMARY KEY, name TEXT, population INTEGER)";
        $this->conn->executeQuery($q1);

        $q2 = "INSERT INTO cities(name, population) VALUES('Bratislava', 432000)";
        $this->conn->executeQuery($q2);

        $q3 = "INSERT INTO cities(name, population) VALUES('Budapest', 1759000)";
        $this->conn->executeQuery($q3);

        $q4 = "INSERT INTO cities(name, population) VALUES('Prague', 1280000)";
        $this->conn->executeQuery($q4);

        $q5 = "INSERT INTO cities(name, population) VALUES('Warsaw', 1748000)";
        $this->conn->executeQuery($q5);

        $q6 = "INSERT INTO cities(name, population) VALUES('Los Angeles', 3971000)";
        $this->conn->executeQuery($q6);

        $q7 = "INSERT INTO cities(name, population) VALUES('New York', 8550000)";
        $this->conn->executeQuery($q7);

        $q8 = "INSERT INTO cities(name, population) VALUES('Edinburgh', 464000)";
        $this->conn->executeQuery($q8);

        $q9 = "INSERT INTO cities(name, population) VALUES('Berlin', 3671000)";
        $this->conn->executeQuery($q9);

        $output->writeln('table successfully created');

        return Command::SUCCESS;
    }
}

This is a command that creates the cities table in the database.

$ php bin/console app:create-table

The command is executed.

src/Repository/CityRepository.php
<?php

namespace App\Repository;

use Doctrine\DBAL\Driver\Connection;

class CityRepository
{
    protected $conn;

    public function __construct(Connection $conn)
    {
        $this->conn = $conn;
    }

    public function all(): array
    {
        $queryBuilder = $this->conn->createQueryBuilder();
        $queryBuilder->select('*')->from('cities');
        $stm = $queryBuilder->execute();

        return $stm->fetchAll();
    }
}

In the CityRepository, we have the all method which retrieves all rows. We use the Doctrine DBAL to execute the database query.

src/Controller/CityController.php
<?php

namespace App\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\Routing\Annotation\Route;
use App\Repository\CityRepository;

class CityController extends AbstractController
{
    /**
     * @Route("/city", name="city")
     * @param CityRepository $cityRepository
     * @return JsonResponse
     */
    public function index(CityRepository $cityRepository): JsonResponse
    {
        $cities = $cityRepository->all();

        return $this->json([
            'cities' => $cities
        ]);
    }
}

Inside the controller, we fetch all data using the CityRepository. The data transformed into JSON with the json helper.

$ symfony serve

We start the web server and locate to the localhost:8000/city.

PHP JSON in Laravel

In the following example, we send a JSON response from a Laravel application.

$ laravel new larajson
$ cd larajson

We create a new Laravel application.

.env
...
DB_CONNECTION=sqlite
DB_DATABASE=/tmp/ydb.db
...

We define the connection and the database file.

$ touch /tmp/ydb.db

We create the database file.

$ php artisan make:migration create_cities_table
Created Migration: 2020_07_25_101535_create_cities_table

A new migration for the cities table is created.

database/migrations/2020_07_25_101535_create_cities_table.php
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class CreateCitiesTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('cities', function (Blueprint $table) {
            $table->id();
            $table->string('name');
            $table->integer('population');
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('cities');
    }
}

In the migration file, we create a schema for the cities table.

$ php artisan migrate

We run the migrations.

$ php artisan make:seeder CitySeeder

We create a data seeder for our database.

database/seeds/CitySeeder.php
<?php

use Illuminate\Database\Seeder;

class CitySeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
        DB::table('cities')->insert([
            ['name' => 'Bratislava', 'population' => 432000],
            ["name" => "Budapest", "population" => 1759000],
            ["name" => "Prague", "population" => 1280000],
            ["name" => "Warsaw", "population" => 1748000],
            ["name" => "Los Angeles", "population" => 3971000],
            ["name" => "New York", "population" => 8550000],
            ["name" => "Edinburgh", "population" => 464000],
            ["name" => "Berlin", "population" => 3671000]
        ]);
    }
}

The seeder inserts eight rows into the table.

$ php artisan db:seed --class=CitySeeder

We execute the seeder.

routes/web.php
<?php

use Illuminate\Support\Facades\Route;
use Illuminate\Http\JsonResponse;

Route::get('/city', function (): JsonResponse {

    $cities = DB::table('cities')->get();

    return response()->json([
        'cities' => $cities
    ]);
});

Inside the route, we select all data from the database and return it as JSON with the json helper.

$ php artisan serve

We run the web server.

$ curl localhost:8000/city
{"cities":[{"id":"1","name":"Bratislava","population":"432000"},
{"id":"2","name":"Budapest","population":"1759000"},
{"id":"3","name":"Prague","population":"1280000"},
{"id":"4","name":"Warsaw","population":"1748000"},
{"id":"5","name":"Los Angeles","population":"3971000"},
{"id":"6","name":"New York","population":"8550000"},
{"id":"7","name":"Edinburgh","population":"464000"},
{"id":"8","name":"Berlin","population":"3671000"}]}

We get the data with the curl tool.

PHP JSON from client

Symfony provides the HttpClient component which enables us to create HTTP requests in PHP. We can also send JSON data.

$ composer req symfony/http-client

We install the symfony/http-client component.

send_json_req.php
<?php

require('vendor/autoload.php');

use Symfony\Component\HttpClient\HttpClient;

$httpClient = HttpClient::create();
$response = $httpClient->request('GET', 'http://localhost:8000/greet',  [
    'json' => [
        'name' => 'Lucia',
        'message' => 'Cau'
    ]
]);

$content = $response->getContent();
echo $content . "\n";

The example sends a GET request with a JSON payload.

src/Controller/GreetController.php
<?php

namespace App\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Request;

class GreetController extends AbstractController
{
    /**
     * @Route("/greet", name="greet")
     */
    public function index(Request $request): Response
    {
        $params = [];
        if ($data = $request->getContent()) {
            $params = json_decode($data, true);
        }

        $msg = "{$params['name']} says {$params['message']}";

        $textResponse = new Response($msg , 200);
        $textResponse->headers->set('Content-Type', 'text/plain');

        return $textResponse;
    }
}

In the GreetController, we process the JSON data and generate a text message, which is returned to the client.

$ php send_json_req.php 
Lucia says Cau

This is the output.

In this article we have worked with JSON data in plain PHP, Symfony, Slim, and Laravel.

Author

My name is Jan Bodnar and I am a passionate programmer with many years of programming experience. I have been writing programming articles since 2007. So far, I have written over 1400 articles and 8 e-books. I have over eight years of experience in teaching programming.

List all PHP tutorials.