biensure/signal

An API Swiss Army Knife, extend this Signal class with methods and create it

v2.0.1 2025-01-30 18:55 UTC

This package is not auto-updated.

Last update: 2025-05-08 20:15:48 UTC


README

  • An API Swiss Army Knife

  • Extend this Signal class with methods and do "new YourSignalClassExtension()"

  • Name your methods like "GETcontacts", "POSTlogin" and all of them will be callable: GET /contacts, POST /login

  • Use Signal class functions to read a JSON/XML/TEXT body() into an array and ("ANSWER") to any request in a vew lines of PHP

Run

Method 1

Clone the repo to get the files and run composer install

# clone and change dir
git clone https://gitlab.com/biensure/signal && cd ./signal && composer install

Create a file named index.php with the following content

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

// extend Biensure\Signal
class SignalExtensionExample extends Biensure\Signal
{
    public function GET()
    {
        self::_("<h1>HOME</h1>\n<a href='./api'>API info</a>", 200, "HTML");
    }

    public function GETapi()
    {
        self::_($this->getApiArray());
    }
}

// create your extension
new SignalExtensionExample();

Create a file named .htaccess with the following content:

RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^ index.php [QSA,L]

For shared hosting

Use your ftp client to upload your files to your ../public, ../htdocs folder and visit your website

For local apache

Place the files of your project folder to in your /var/www/html folder and open your browser to http://localhost

Method 2

Create your own project, do composer require and use php build-in webserver

# composer
composer require biensure/signal

# run php build in server to run the example
php -S localhost -t ./signal/tests/ ./signal/tests/index.example

# or create an index.php file as described in "Method 1" an do
php -S localhost -t ./ ./index.php

Open your browser to http://localhost

Subdirectory installation

When moving your project folder to a subdirectory like: "/v1/".
It is necessary to define the static $BASE_PATH variable in your extension class

...
// extend Biensure\Signal
class SignalExtensionExample extends Biensure\Signal
{
    public static $BASE_PATH = "/v1/";
    ...

For shared hosting and apache please change your .htaccess file to this:

RewriteEngine On
RewriteBase /v1/
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^ index.php [QSA,L]

Usage

These examples are served by the php build in server, see above "Method 2"

GET / - HOME

Create a file named index.php and extend the Biensure\Signal class as described in "Method 1" above.

Check if your home page displays probably: "HOME" and a link "API info".

GET /info - URI to function

Add this to the extention class:

    public function GETinfo(int $i, string $s = "some", ...$uri)
    {
        $o = "<h1>GETinfo</h1>";
        $o .= "<pre>";
        $o .= "\n headers> ". var_export(self::_headers(), true);
        $o .= "\n i> ". var_export($i, true);
        $o .= "\n s> ". var_export($s, true);
        $o .= "\n uri> ". var_export($uri, true);
        $o .= "\n vars> ". var_export(self::_vars(), true);
        $o .= "</pre>";
        self::_($o, 200, "HTML"); // "HTML" is needed because the default is "JSON"
    }
  • Open http://localhost/info/12 points to your extended Biensure/Signal class the function GETinfo injects "12" as required parameter
  • Try http://localhost/info/text and see the error, this is because $i is defined to be an (int). Watch out! - definition of (array) is not supported.
  • Vist http://localhost/info/12/other and see that the default value of $s is set to "other"
  • Now adding more URI will create and grow the (variadic) $uri.
  • Overload of arguments is possible because of the (variadic) ...$uri variable defined in the function.
  • Removing this variable from the function will throw an "404 - Not Found" on visiting any link longer then: http://localhost/info/12/other/

GET /login, POST /login - Flag authorization

Add this to the extention class:

    public function GETlogin()
    {
        $flagStatus = self::getSessionFlag();
        if ( $flagStatus <= 0 ) {
            $o = '<h3>Login</h3>';
            $o .= '<form method="POST" action="login">';
            $o .= '<div>User: </div><input name="user" type="text"> # john<br>';
            $o .= '<div>Password: </div><input name="password" type="text"> # doit<br><br>';
            $o .= '<input type="submit" value="submit">';
            $o .= '</form>';
        } else {
            $o = '<h3>Logout</h3>';
            $o .= '<form method="POST" action="logout">';
            $o .= '<input type="submit" value="submit">';
            $o .= '</form>';
        }
        self::_($o, 200, "HTML");
    }

    public function POSTlogin()
    {
        $vars = self::_colOrCrash( ["user", "password"], array_merge(self::_vars(), self::_body()) );
        if ( $vars['user'] == "john" && $vars['password'] == "doit" ) {
            self::setSessionFlag(100);
            $o = ["success" => true];
        } else
            $o = ["success" => false];
        self::_($o);
    }

    public function GETlogstatus()
    {
        $flagStatus = self::getSessionFlag();
        self::_(["logstatus" => $flagStatus]);
    }

    public function GETsecret()
    {
        self::flag(100); // authorization check
        self::_("go for heavenly treasure");
    }

The Flag functionality is there to keep unauthorized users to see the /secret.
Logging in sets the Flag authorization level to 100 and gives the user privilege to see the /secret.

  • self::getSessionFlag() function in GETlogin gets the current value of the flag variable in the session
  • self::_vars() function reads the _GET and _POST variables
  • self::_body() reads any body variables ("JSON" by default, but can be "XML" or "TEXT" lines too) into an array. Attention! this function can only be called once.
  • self::_colOrCrash( ["user", "password"], array_merge(self::_vars(), self::_body()) ) function in POSTlogin reads "user" and "password" or throws a 422
  • self::_colOrNull() is an alternative to _colOrCrash() that reads variables by name, but returns NULL for unfound variables.
  • self::setSessionFlag(100) in POSTlogin sets the Flag to 100 in session variable
  • self::flag(100) function in GETsecret checks if the session contains a flag with at least a value of 100 else it throws a 404

PATCH example using curl

Add this to the extention class:

    public function PATCHschema()
    {
        self::flag(100); // authorization check
        $vars = self::_colOrNull(["name", "schema"], self::_body()); // get "name" and "schema"
        self::_(["name" => $vars['name'], "schema" => $vars['schema']]);
    }

Then use curl to login and save the session cookie

curl -c cookies.txt -H 'Content-Type: application/json' -d '{ "user":"john", "password": "doit"}' -X POST http://localhost/login

See that you ar logged in while using that session cookie:

curl -b cookies.txt -H 'Content-Type: application/json' -X GET http://localhost/logstatus

And do the PATCH schema with session cookie

curl -b cookies.txt -H 'Content-Type: application/json' -d '{ "name": "test", "schema": { "type" : "string" } }' -X PATCH http://localhost/schema

Try the same thing without the session cookie

curl -H 'Content-Type: application/json' -d '{ "name": "test", "schema": { "type" : "string" } }' -X PATCH http://localhost/schema

GET /api

Add the following PHP Documentation to your functions:

   /**
    * Shows the API information
    *
    * @return JSON string
    */
    public function GETapi()
    {
        self::_($this->getApiArray());
    }
  • Opening your browser to http://localhost/api will show you an JSON formatted description of the total API.
  • Automatically all PHP Documentation of the functions is reflected into this view.

Configuration

To configure your API, add/override the following static variables:

...
class SignalExtensionExample extends Biensure\Signal
{
    public static $BASE_PATH = ""; // "/v1/", "/tests/" or any project subfolder your index.php is placed
    public static $SESSION_FLAG_NAME = "_BIENSURE_SIGNAL_FLAG";
    public static $TYPE = "JSON"; // standard communication type: "JSON", HTML", "TEXT", "XML", "MARKDOWN" or "RAW"
    public static $METHODS = ["GET", "DELETE", "CONNECT", "HEAD", "OPTIONS", "TRACE", "PATCH", "POST", "PUT"]; // possible HTTP methods
    public static $HTML_HEAD = '<!DOCTYPE html><html lang="en"><head><title>'.__CLASS__.'</title><link rel="stylesheet" href="https://unpkg.com/sakura.css/css/sakura.css"></head><body><div>';
    public static $HTML_FOOT = '</div></body></html>'; // override both HTML_HEAD and HTML_FOOT with empty '' if unwanted
...

Issues

License

MIT

Project status

In development, but tested and in production for my projects like https://gitlab.com/biensure/pitbox