About

PSX is a framework written in PHP to create REST APIs. It provides tools to handle routing, API versioning, data transformation, authentication, documentation and testing. With PSX you can easily build a REST API around an existing application or create a new one from scratch. Take a look at the example to see an API in action. The following chapter shows some features of PSX which should give you a first impression how it works.

Documentation

PSX provides tools to automatically generate a documentation from the defined API controllers. The documentation is generated from the code and is therefore always up to date. To see a documentation you can view the example project or take a look at the source.

Schema definition

PSX gives you the possibility to define a clear schema for each API endpoint. The schema can be defined through annotations, as RAML specification or directly in PHP. Because PSX has all informations how the API is structured it can validate incoming and outgoing data according to the schema.

Based on the defined schema PSX can also generate different API definition formats like: RAML and Swagger. These formats can be used i.e. to automatically generate client-side code.

class Endpoint extends SchemaApiAbstract
{
    /**
     * Returns a collection of population entries
     *
     * @QueryParam(name="startIndex", type="integer")
     * @QueryParam(name="count", type="integer")
     * @Outgoing(code=200, schema="Acme\Collection")
     */
    public function doGet()
    {
    }

    /**
     * @Incoming(schema="Acme\Entry")
     */
    public function doPost($entry)
    {
    }
}
class Endpoint extends SchemaApiAbstract
{
    /**
     * @Incoming(schema="Acme\News")
     */
    protected function doPost($news);
    {
        // @TODO do something with the news
        $news->getTitle();
        $news->getAuthor()->getName();
        $news->getContent();

        return array(
            'success' => true,
            'message' => 'Record successful created',
        );
    }
}
/**
 * @Title("news")
 * @Required({"content"})
 */
class News
{
    /**
     * @Type("string")
     * @Description("Title description")
     * @Pattern("[A-z]{3,16}")
     */
    protected $title;

    /**
     * @Ref("Acme\Author")
     */
    protected $author;

    /**
     * @Type("string")
     * @MinLength(3)
     * @MaxLength(512)
     */
    protected $content;

    // getter/setter
}

Request parsing

PSX parses the incoming request into an object graph according to the defined data model. The data model can be defined as simple POPO which can contain annotations to describe the schema.

In the example we see an API controller which consumes the request data of a POST request. In the tabs you can see the used data model.

Response generation

PSX analyzes the data structure of the response and uses a data writer to generate the fitting response format. By default PSX comes with multiple data writers which can generate different data formats like i.e. JSON or XML. The writer which gets used depends on the Accept header field or the GET parameter format. In the example you can see a controller and the corresponding responses from different data writers.

class Endpoint extends SchemaApiAbstract
{
    protected function doGet();
    {
        return [
            'totalResults' => 2,
            'entry' => [[
                'title' => 'Acme news one',
                'content' => 'lorem ipsum'
            ],[
                'title' => 'Acme news two',
                'content' => 'lorem ipsum'
            ]],
        ];
    }
}
{
    "totalResults": 2,
    "entry": [
        {
            "title": "Acme news one",
            "content": "lorem ipsum"
        },
        {
            "title": "Acme news two",
            "content": "lorem ipsum"
        }
    ]
}
<?xml version="1.0" encoding="UTF-8"?>
<record>
 <totalResults>2</totalResults>
 <entry>
  <title>Acme news one</title>
  <content>lorem ipsum</content>
 </entry>
 <entry>
  <title>Acme news two</title>
  <content>lorem ipsum</content>
 </entry>
</record>
<?xml version="1.0" encoding="UTF-8"?>
<entry xmlns="http://www.w3.org/2005/Atom">
 <content type="application/xml">
  <record>
   <totalResults>2</totalResults>
   <entry>
    <title>Acme news one</title>
    <content>lorem ipsum</content>
   </entry>
   <entry>
    <title>Acme news two</title>
    <content>lorem ipsum</content>
   </entry>
  </record>
 </content>
</entry>
<?xml version="1.0" encoding="UTF-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
 <soap:Body>
  <getResponse xmlns="http://phpsx.org/2014/data">
   <totalResults>2</totalResults>
   <entry>
    <title>Acme news one</title>
    <content>lorem ipsum</content>
   </entry>
   <entry>
    <title>Acme news two</title>
    <content>lorem ipsum</content>
   </entry>
  </getResponse>
 </soap:Body>
</soap:Envelope>
<?xml version="1.0" encoding="UTF-8"?>
<json:object xmlns:json="http://www.ibm.com/xmlns/prod/2009/jsonx">
 <json:number name="totalResults">2</json:number>
 <json:array name="entry">
  <json:object>
   <json:string name="title">Acme news one</json:string>
   <json:string name="content">lorem ipsum</json:string>
  </json:object>
  <json:object>
   <json:string name="title">Acme news two</json:string>
   <json:string name="content">lorem ipsum</json:string>
  </json:object>
 </json:array>
</json:object>
> vendor/bin/psx api spec.raml php > Controller.php
> vendor/bin/psx schema schema.json php > Model.php

Code generation

PSX provides a CLI command to generate different classes. I.e. it is possible to generate a POPO based on a jsonschema or to generate a controller class based on a RAML specification. These generated classes can then be used to rapidly develop the defined API endpoint.

API testing

Because PSX is build around a HTTP request and response object we can easily test our API code. We dont need to start a webserver or mock the request we can simply call our controller from the test. Internally this is the same code as when we make a call to the webserver except that we manually create the HTTP request and response.

In this way we can easily make an integration test for the API endpoint by looking at the actual response. In our example we call the GET method of the controller and check whether the response is a JSON object {"hello": "world"}.

class HelloWorldApiTest extends ControllerTestCase
{
    public function testHelloWorld()
    {
        // send request to the controller
        $response = $this->sendRequest('http://127.0.0.1/foo', 'GET');
        $body     = (string) $response->getBody();

        $this->assertEquals(200, $response->getStatusCode());
        $this->assertJsonStringEqualsJsonString('{"hello": "world"}', $body);
    }

    protected function getPaths()
    {
        return [
            [['GET'], '/foo', 'Acme\HelloWorldController'],
        ];
    }
}
GET      /news             Acme\News\Application\Index::doIndex
GET      /news/:news_id    Acme\News\Application\Index::doDetail
GET      /bar/$foo<[0-9]+> Acme\News\Application\Article
GET      /download/*file   Acme\News\Application\Download
GET|POST /bar              Acme\News\Application\BarApi
class Index extends SchemaApiAbstract
{
    public function doGet()
    {
        $newsId = $this->getUriFragment('news_id');

        // @TODO work with the news id
    }
}

Routing

PSX uses a simple routing file which was inspired by the Java Play-Framework. We can specify the allowed request methods, the path and the controller which should be called. In the example controller we access the dynamic part of the path.