Context package

Ultraligth validation and sanitization for RESTful APIs.

Author:
Enrico Fagnoni - E-Artspace

Abstract

This package exports a set of class to manage the context of a RESTful request.

A Context is defined as the full set of variables available runtime durin the serving of an http request. Variables are grouped in distinct name spaces.

All variables in name spaces are read only.

This package exposes the methods to sanitize and validate context variables.

Note that cookies and session aren't part of context. This because proper RESTful resource implementation shouldn't need persisten status in any way.

License

Copyright © 2013 by E-Artspace S.r.L.® GPL-3.0+

This code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3.0of the License, or (at your option) any later version. This code is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

For commercial license or others license schema, please contact E-Artspace or authors.

Installation

This package follows BOTK guide line for installation and require composer.

Add following dependances to composer.json file in your project root:

{
    "require": {
        "botk/context": "*"
    }
}

The Context class

A Context is the full set of all that a CGI endpoint knows about  Request beside its URI. Context space contains system environment variables, requests header, request body, server specific vars, cookies, variables in configuration files, local define variables, etc etc..

Context variables are grouped in namespaces. BOTK\Context\Context class provide the method  ns(namespace name) to access name spaces. There are five predefined namespaces:

  1. the variables defined in the heading of the request and exposed by web server (INPUT_SERVER);
  2. the variables encoded in http URI (INPUT_GET);
  3. the variables encoded in http body (INPUT_POST);
  4. the system environment (INPUT_ENV);
  5. local defined variables (Context::LOCAL).

Beside these, Context class allow you to define dynamic namespace built from .ini files loaded runtime from a configuration directory. The name of the namespace is the name of the configuration file without .ini extension.

The configuration file must exists when is first referenced. The default configuration directory is ../configs, that normally is a directory placed at the same level of the directory that contains the scripts who instance a context class. You can change the default directory defining the environment variable $_ENV['BOTK_CONFIGDIR'].

Context shold be considered as a readonly data structure because you cant change the resource execution context.

For example suppose to have a end-point script is in myapp/httdocs/index.php so, to get a success on following code, it must exist myapp/configs/sample.ini file:

use BOTK\Context\Context as CX;
$sampleNameSpace = CX::factory()->ns('sample');

If you want to put the .ini file in the same end-point directory:

use BOTK\Context\Context as CX;
$_ENV['BOTK_CONFIGDIR'] = '.';
$sampleNameSpace = CX::factory()->ns('sample');

You can also access to local variable passing them to context constructor:

$myvar = 'ok'; // define a variable in local scope
$v = CX::factory(get_defined_vars())->ns(CX::LOCAL)->getValue('myvar');
//$v == 'ok'

Context class exposes the method guessRequestCanonicalUri() that returns the current request uri in canonical form and the method guessRequestRelativelUri() that returns it as relative path.

Please note that get request uri is not a trivial job, this because http servers and proxyes can change what the user entered. You can just guess about it.

The ContextNameSpace Class

This class implements the controlled access to set of variables.

The getValue() method

The BOTK\Context\ContextNameSpace class exports the method getValue() that allows to validate and sanitize variables defined in a namespace (see PHP Validation & Sanitization article).

If somethings goes wrong it throwns an error that can be trapped by standard BOTK error management.

getValue() exposes following interface:

  param string $varName       the name of a variable defined in the namespace
  param mixed  $default       a default value if variable is not set. NULL means that
                              the variable is mandatory.
  param mixed  $validator     Can be:
                                1) null use default validation
                                2) an integer representing a simple Validate filter without flags and options
                                3) an array representing a Validate filter with flags and/or options
                                4) an instance of an object that expose function 'assert'.
                                   you can use istance of Respect\Validation\Validator
  param mixed  $sanitizer     Can be:
                                A) null if no sanitization is required
                                B) an integer representing a simple Sanitize filter without flags and options
                                C) an array representing a Sanitize filter with flags and/or options
                                D) a callable that accept an argument (the source) and return sanitized one 
 
 throws InvalidArgumentException if $varName is not defined and $default not provided
 throws Exception if validator fails to validate variable (depending from the validator)
 throws InvalidArgumentException if sanitize fails
 
 return mixed the sanitized value associated to varName in namespace
 
 LIMITS:
  sanitizer are supported just on scalar values
  non scalar value require a custom validator like Respect\Validator
 

In addition to getValue() ContextNameSpace class expose a set of shortcuts for easy to read and write code.

validator shortcucts

Some validators shold be boring to write, here are a set of predefined shortcuts:

     public static function ENUM($wlist)
     {
         return  array(
                'filter'    => FILTER_VALIDATE_REGEXP,
                'options'   => array('regexp' => "/^($wlist)$/")
         );
     }
     
     public static function STRING($pattern)
     {
         return  array(
                'filter'    => FILTER_VALIDATE_REGEXP,
                'options'   => array('regexp' => $pattern)
         );
     }

     public static function FILENAME()
     {
         return  array(
                'filter'    => FILTER_VALIDATE_REGEXP,
                'options'   => array('regexp' => '^[\w,\s-]+\.[A-Za-z]+$')
         );
     }  


     public static function POSITIVE_INT()
     {
         return  array(
                'filter'    => FILTER_VALIDATE_INT,
                'options'   => array("min_range"=>1)
         );
     }


     public static function NON_NEGATIVE_INT()
     {
         return  array(
                'filter'    => FILTER_VALIDATE_INT,
                'options'   => array("min_range"=>0)
         );
     }  


getValue shortcucts

Even with validator shortcuts, a call to getValue method can be too verbose, here are some shortcut you can use for common task:


PagedResourceContext class

The PagedResource class is a facility to manage the context of a resource whose query string can contains variables to drive pagination processing. It is a specialization of Context class and like Context should be considered as a read-only data structure.

The resource paging context is inspired on W3C's Linked Data Platform Paging specifications .Here are some imported definitions:
Paged resource
A resource  whose representation may be too large to fit in a single HTTP response, for which a server offers a sequence of single-page resources. A paged P is broken into a sequence of pages (single-page resources) P1, P2, ...,Pn, the representation of each Pi contains a subset of  P..
Single-page resource
One of a sequence of related resources P1, P2, ...,Pn, each of which contains a subset of the state of another resource P. P is called the paged resource. For readers familiar with paged feeds, a single-page resource is similar to a feed document and the same coherency/completeness considerations apply.

Note: the choice of terms was designed to help authors and readers clearly differentiate between the resource being paged, and the individual page resources, in cases where both are mentioned in close proximity.

first page
The uri to the first single-page resource of a paged resource P. For example http://www.example.org/bigresource?page=0
next page
The uri  to the next single-page resource of a paged resource P.
last page
The uri  to the last single-page resource of a paged resource P.
previous page
The uri  to the previous single-page resource of a paged resource P.

Pagination require some variable to be specified in resource URI query strings. Such variables name and other default values can be passed to class constructor as an option associative array. Here are supported keywords:

It exposes following methods:

getSinglePageResourceUri( $pagenum = null, $pagesize=null)
returns the canonical uri with page variables in query string. Without arguments returns current page resource uri
getPagedResourceUri()
return the resource uri wit page variables removed from query string 
isPagedResource()
returns true is resource uri contains page info
getPageNum()
returns the current page num
getPageSize()
returns the size of the page
firstPageUri()
returns the Resource uri for first Single page resource
nextPageUri()
returns the Resource uri for next page resource or the current page resource uri
prevPageUri()
returns the Resource uri for next page resource or the first page resource uri
hasNextPage()
returns true if a next page is available. By default returns true. It can be affected by declareActualPageSize() method
isLastPage()
returns true if the current is the last page. By default returns false. It can be affected by declareActualPageSize() method
declareActualPageSize($count)
inform context of the number of item in current Page, by default is 0. This affects hasNextPage() and isLastPageMethods().

Here is how declaretActualPageSize() affects hasNextPage() and isLastPage():

    function hasNextPage(){
         return actualPageSize == $this->pagesize;
    }
      
    function isLastPage(){
          return actual PageSize < $this->pagesize;
    }