Автоматическое конвертирование JSON в PHP-объекты

Автоматическое конвертирование JSON в PHP-объекты

Автоматическое конвертирование JSON в PHP-объекты

Доброго времени суток! В данной статье я покажу Вам один из способов автоматической конвертации
JSON строки в объекты PHP
. Т.е. представим следующую ситуацию — у нас есть некий API-сервис
(в нашем примере Jsonplaceholder), запросы к которому отдают ответы в JSON-формате.
Эти ответы потом нужно преобразовать в нечто такое, с чем мы дальнейшем можем работать в PHP.
Но благодаря библиотеке square/pjson и аттрибутам PHP мы можем максимально автоматизировать этот
процесс.

Итак, структура проекта следующая:



.

├── src

│   ├── Api

│   │   ├── Api.php

│   │   ├── Endpoint.php

│   │   ├── PhotosEndpoint.php

│   │   ├── PostEndpoint.php

│   │   └── UserEndpoint.php

│   ├── Objects

│   │   ├── Address.php

│   │   ├── Comment.php

│   │   ├── Company.php

│   │   ├── Geo.php

│   │   ├── Photo.php

│   │   ├── Post.php

│   │   └── User.php

│   └── index.php

├── composer.json

├── composer.lock





В папке src/Api у нас будут классы отвечающие за сетевое общение с разными сущностями, будь то статья (Post),
пользователь (User) и т.д. В папке src/Objects — сами объекты, в которые мы будем конвертировать JSON-ответ.

Итак, разберем содержимое каждой папки по порядку.

src/Objects/Post.php



<?php



namespace Objects;



// импортируем классы из библиотеки

use SquarePjsonJson;

use SquarePjsonJsonSerialize;



class Post

{

    // добавляем методы из трейта в класс Post

    use JsonSerialize;



    // каждое свойство класса Post соответствует свойству из JSON ответа

    // аттрибут #[Json] обязателен 

    #[Json]

    public int $id;



    #[Json]

    public int $userId;



    #[Json]

    public string $title;



    #[Json]

    public string $body;

}



Класс выше описывает следующий JSON-ответ:



{

    "id": 1,

    "userId": 1,

    "title": "Post title",

    "body": "Post text"

}





Т.е. как видно из примера, когда мы получим ответ от сервера, значения полей этого JSON-ответа станут значениями полей класса
автоматически. Причем, типы данных полей класса могут быть не только встроенными в PHP типами, но и пользовательскими типами —
классами. Пример ниже:



// User.php



<?php



namespace Objects;





use SquarePjsonJson;

use SquarePjsonJsonSerialize;





class User

{

    use JsonSerialize;



    #[Json]

    public int $id;



    #[Json]

    public string $name;



    #[Json]

    public string $username;



    #[Json]

    public string $email;



    #[Json]

    public Address $address;



    #[Json]

    public string $phone;



    #[Json]

    public string $website;



    #[Json]

    public Company $company;

}





class Company

{

    use JsonSerialize;



    #[Json]

    public string $name;



    #[Json]

    public string $catchPhrase;



    #[Json]

    public string $bs;

}





class Address

{

    use JsonSerialize;



    #[Json]

    public string $street;



    #[Json]

    public string $suite;



    #[Json]

    public string $city;



    #[Json]

    public string $zipcode;



    #[Json]

    public Geo $geo;

}



class Geo

{

    use JsonSerialize;



    #[Json]

    public float $lat;



    #[Json]

    public float $lng;

}



Теперь рассмотрим классы, отвечающие за взаимодействие с сетью и преобразование JSON-ответов в соответствуюшие объекты.
Базовый класс ApiEndpoint заключает в себе базовую логику работы с сетью. В его задачи входит обратиться к удаленному
серверу и получить JSON-ответ.

src/Api/Endpoint.php



<?php



namespace Api;



use Exception;

use ObjectsComment;

use ObjectsPhoto;

use ObjectsPost;





class Endpoint

{

    /**

     * @throws Exception

     */

    protected function endpoint(string $part): bool|string

    {

        $url = sprintf('%s/%s', 'https://jsonplaceholder.typicode.com', $part);

        return $this->fetch($url);

    }



    /**

     * @throws Exception

     */

    private function fetch($uri): string

    {

        $handle = curl_init();



        curl_setopt($handle, CURLOPT_URL, $uri);

        curl_setopt($handle, CURLOPT_POST, false);

        curl_setopt($handle, CURLOPT_HEADER, true);

        curl_setopt($handle, CURLOPT_RETURNTRANSFER, true);

        curl_setopt($handle, CURLOPT_CONNECTTIMEOUT, 10);



        $response      = curl_exec($handle);

        $headerLength  = curl_getinfo($handle, CURLINFO_HEADER_SIZE);

        $httpCode      = curl_getinfo($handle, CURLINFO_HTTP_CODE);

        $body          = substr($response, $headerLength);



        if ($httpCode != 200) {

            throw new Exception('Network problem', $httpCode);

        }



        return $body;

    }

}



Остальные классы из пространства Api наследуются от него:

ApiPhotosEndpoint



<?php



namespace Api;



use Exception;

use ObjectsPhoto;



class PhotosEndpoint extends Endpoint

{



    /**

     * @return array<Photo>

     * @throws Exception

     */

    public function getPhotos(int $limit = 10): array

    {

        return array_slice(Photo::listFromJsonString($this->endpoint('photos')), 0, $limit);

    }



}



ApiPostEndpoint



<?php



namespace Api;



use Exception;

use ObjectsComment;

use ObjectsPost;



class PostEndpoint extends Endpoint

{

    /**

     * @return array<Post>

     * @throws Exception

     */

    public function getPosts(int $limit = 10): array

    {

        return array_slice(Post::listFromJsonString($this->endpoint('posts')), 0, $limit);

    }



    /**

     * @param int $id

     * @return Post

     * @throws Exception

     */

    public function getPost(int $id): Post

    {

        return Post::fromJsonString($this->endpoint(sprintf('posts/%d', $id)));

    }





    /**

     * @return array<Comment>

     * @throws Exception

     */

    public function getPostComments(int $id): array

    {

        return Comment::listFromJsonString($this->endpoint(sprintf('posts/%d/comments', $id)));

    }

}

Также есть класс ApiApi, который используется как центральное место для доступа ко всем API-endpoint:



<?php



namespace Api;





class Api

{

    public static function posts()

    {

        return new PostEndpoint();

    }



    public static function photos()

    {

        return new Endpoint();

    }



    public static function users()

    {

        return new UserEndpoint();

    }

}



Вызывается все это дело следующим образом:



<?php



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





function m_print(string ...$messages): void

{

    foreach ($messages as $message) {

        print $message;

        print PHP_EOL;

    }

    print PHP_EOL;

    print '++++++++++++++++++++++++++++++++' . PHP_EOL;

}





(function()

{

    try {



        $user = ApiApi::users()->getUser(1);



        m_print($user->toJson(JSON_PRETTY_PRINT));



        m_print('Address', $user->address->toJson(JSON_PRETTY_PRINT));



        m_print('Address.Geo', $user->address->geo->toJson(JSON_PRETTY_PRINT));



        m_print('Company', $user->company->toJson(JSON_PRETTY_PRINT));

    }

    catch (Exception $e)

    {

        if(404 === $e->getCode()) print($e->getMessage());

    }





})();











Результат:



$ php src/index.php

{

    "id": 1,

    "name": "Leanne Graham",

    "username": "Bret",

    "email": "Sincere@april.biz",

    "address": {

        "street": "Kulas Light",

        "suite": "Apt. 556",

        "city": "Gwenborough",

        "zipcode": "92998-3874",

        "geo": {

            "lat": -37.3159,

            "lng": 81.1496

        }

    },

    "phone": "1-770-736-8031 x56442",

    "website": "hildegard.org",

    "company": {

        "name": "Romaguera-Crona",

        "catchPhrase": "Multi-layered client-server neural-net",

        "bs": "harness real-time e-markets"

    }

}



++++++++++++++++++++++++++++++++

Address

{

    "street": "Kulas Light",

    "suite": "Apt. 556",

    "city": "Gwenborough",

    "zipcode": "92998-3874",

    "geo": {

        "lat": -37.3159,

        "lng": 81.1496

    }

}



++++++++++++++++++++++++++++++++

Address.Geo

{

    "lat": -37.3159,

    "lng": 81.1496

}



++++++++++++++++++++++++++++++++

Company

{

    "name": "Romaguera-Crona",

    "catchPhrase": "Multi-layered client-server neural-net",

    "bs": "harness real-time e-markets"

}



++++++++++++++++++++++++++++++++





Таким образом, с помощью библиотеки square/pjson можно сделать автоматическое преобразование JSONPHP Objects,
при этом заметьте, что в Api реализованы только GET-методы, тогда как там же можно добавить еще методы
POST, DELETE и другие.

Источник

НЕТ КОММЕНТАРИЕВ

Оставить комментарий