2017-08-04 16:28:16 +02:00
< ? php
/**
* Classe per la gestione delle API del progetto .
*
* @ since 2.3
*/
class API extends \Util\Singleton
{
2017-08-29 12:42:42 +02:00
/** @var array Elenco delle risorse disponibili suddivise per categoria */
2017-08-04 16:28:16 +02:00
protected static $resources ;
2017-08-29 12:42:42 +02:00
/** @var array Stati previsti dall'API */
2017-08-04 16:28:16 +02:00
protected static $status = [
'ok' => [
'code' => 200 ,
'message' => 'OK' ,
],
'internalError' => [
'code' => 400 ,
'message' => " Errore interno dell'API " ,
],
'unauthorized' => [
'code' => 401 ,
'message' => 'Non autorizzato' ,
],
'notFound' => [
'code' => 404 ,
'message' => 'Non trovato' ,
],
'serverError' => [
'code' => 500 ,
'message' => 'Errore del server' ,
],
2017-08-31 10:09:06 +02:00
'incompatible' => [
'code' => 503 ,
'message' => 'Servizio non disponibile' ,
],
2017-08-04 16:28:16 +02:00
];
2017-08-29 12:42:42 +02:00
/**
* @ throws InvalidArgumentException
*/
public function __construct ()
2017-08-04 16:28:16 +02:00
{
2017-09-04 16:04:45 +02:00
if ( ! self :: isAPIRequest () || ( ! Auth :: check () && self :: getRequest ()[ 'resource' ] != 'login' )) {
2017-08-04 16:28:16 +02:00
throw new InvalidArgumentException ();
}
}
2017-08-29 12:42:42 +02:00
/**
* Gestisce le richieste di informazioni riguardanti gli elementi esistenti .
*
* @ param array $request
*
* @ return string
*/
public function retrieve ( $request )
2017-08-04 16:28:16 +02:00
{
2017-09-04 10:24:44 +02:00
$user = Auth :: user ();
2017-09-22 09:58:29 +02:00
// Controllo sulla compatibilità dell'API
if ( ! self :: isCompatible ()) {
return self :: response ([
'status' => self :: $status [ 'incompatible' ][ 'code' ],
]);
}
2017-08-04 16:28:16 +02:00
$table = '' ;
$select = '*' ;
2017-09-04 16:04:45 +02:00
$where = [];
$order = [];
2017-08-04 16:28:16 +02:00
// Selezione personalizzata
2017-09-04 16:04:45 +02:00
$select = ! empty ( $request [ 'display' ]) ? explode ( ',' , substr ( $request [ 'display' ], 1 , - 1 )) : $select ;
2017-08-04 16:28:16 +02:00
// Ricerca personalizzata
2017-09-04 16:04:45 +02:00
foreach (( array ) $request [ 'filter' ] as $key => $value ) {
// Rimozione delle parentesi
2017-08-04 16:28:16 +02:00
$value = substr ( $value , 1 , - 1 );
2017-09-04 16:04:45 +02:00
// Individuazione della tipologia (array o string)
$where [ $key ] = str_contains ( $value , ',' ) ? explode ( ',' , $value ) : $value ;
2017-08-04 16:28:16 +02:00
}
// Ordinamento personalizzato
2017-09-04 16:04:45 +02:00
foreach (( array ) $request [ 'order' ] as $value ) {
2017-08-04 16:28:16 +02:00
$pieces = explode ( '|' , $value );
$order [] = empty ( $pieces [ 1 ]) ? $pieces [ 0 ] : [ $pieces [ 0 ] => $pieces [ 1 ]];
}
2017-09-04 16:04:45 +02:00
// Paginazione automatica dell'API
2017-09-01 18:13:25 +02:00
$page = ( int ) $request [ 'page' ] ? : 0 ;
$length = Settings :: get ( 'Lunghezza pagine per API' );
$database = Database :: getConnection ();
2017-08-04 16:28:16 +02:00
$kind = 'retrieve' ;
$resources = self :: getResources ()[ $kind ];
2017-08-29 12:42:42 +02:00
$resource = $request [ 'resource' ];
2017-08-04 16:28:16 +02:00
2017-09-04 16:04:45 +02:00
if ( in_array ( $resource , array_keys ( $resources ))) {
$dbo = $database ;
2017-08-04 16:28:16 +02:00
2017-09-04 16:04:45 +02:00
// Esecuzione delle operazioni personalizzate
2017-08-04 16:28:16 +02:00
$filename = DOCROOT . '/modules/' . $resources [ $resource ] . '/api/' . $kind . '.php' ;
include $filename ;
2017-09-04 16:04:45 +02:00
} elseif ( ! in_array ( $resource , explode ( ',' , Settings :: get ( 'Tabelle escluse per la sincronizzazione API automatica' )))) {
$table = $resource ;
// Individuazione della colonna AUTO_INCREMENT per l'ordinamento automatico
if ( empty ( $order )) {
$order [] = $database -> fetchArray ( 'SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = ' . prepare ( $table ) . " AND EXTRA LIKE '%AUTO_INCREMENT%' AND TABLE_SCHEMA = " . prepare ( $database -> getDatabaseName ()))[ 0 ][ 'COLUMN_NAME' ];
}
2017-08-04 16:28:16 +02:00
}
// Generazione automatica delle query
if ( empty ( $results ) && ! empty ( $table )) {
try {
2017-09-04 16:04:45 +02:00
// Date di interesse
if ( ! empty ( $request [ 'upd' ])) {
$where [ '#updated_at' ] = 'updated_at >= ' . prepare ( $request [ 'upd' ]);
}
if ( ! empty ( $request [ 'crd' ])) {
$where [ '#created_at' ] = 'created_at >= ' . prepare ( $request [ 'crd' ]);
}
2017-08-04 16:28:16 +02:00
// Query per ottenere le informazioni
2017-09-04 16:04:45 +02:00
$results = $database -> select ( $table , $select , $where , $order , [ $page * $length , $length ]);
2017-08-04 16:28:16 +02:00
// Informazioni aggiuntive
2017-09-04 16:04:45 +02:00
$query = $database -> select ( $table , $select , $where , $order , [], true );
$cont = $database -> fetchArray ( 'SELECT COUNT(*) as `records`, CEIL(COUNT(*) / ' . $length . ') as `pages` FROM (' . $query . ') AS `count`' );
2017-08-04 16:28:16 +02:00
if ( ! empty ( $cont )) {
$results [ 'records' ] = $cont [ 0 ][ 'records' ];
$results [ 'pages' ] = $cont [ 0 ][ 'pages' ];
}
} catch ( PDOException $e ) {
return self :: error ( 'internalError' );
}
}
return self :: response ( $results );
}
2017-08-29 12:42:42 +02:00
/**
* Gestisce le richieste di creazione nuovi elementi .
*
* @ param array $request
*
* @ return string
*/
public function create ( $request )
2017-08-04 16:28:16 +02:00
{
2017-08-29 12:42:42 +02:00
return $this -> fileRequest ( $request , 'create' );
2017-08-04 16:28:16 +02:00
}
2017-08-29 12:42:42 +02:00
/**
* Gestisce le richieste di aggiornamento di elementi esistenti .
*
* @ param array $request
*
* @ return string
*/
public function update ( $request )
2017-08-04 16:28:16 +02:00
{
2017-08-29 12:42:42 +02:00
return $this -> fileRequest ( $request , 'update' );
2017-08-04 16:28:16 +02:00
}
2017-08-29 12:42:42 +02:00
/**
* Gestisce le richieste di eliminazione di elementi esistenti .
*
* @ param array $request
*
* @ return string
*/
public function delete ( $request )
2017-08-04 16:28:16 +02:00
{
2017-08-29 12:42:42 +02:00
return $this -> fileRequest ( $request , 'delete' );
2017-08-04 16:28:16 +02:00
}
2017-08-29 12:42:42 +02:00
/**
* Gestisce le richieste in modo generalizzato , con il relativo richiamo ai file specifici responsabili dell ' operazione .
*
* @ param array $request
*
* @ return string
*/
protected function fileRequest ( $request , $kind )
2017-08-04 16:28:16 +02:00
{
2017-09-04 10:24:44 +02:00
$user = Auth :: user ();
2017-09-22 09:58:29 +02:00
// Controllo sulla compatibilità dell'API
if ( ! self :: isCompatible ()) {
return self :: response ([
'status' => self :: $status [ 'incompatible' ][ 'code' ],
]);
}
2017-08-04 16:28:16 +02:00
$resources = self :: getResources ()[ $kind ];
2017-08-29 12:42:42 +02:00
$resource = $request [ 'resource' ];
2017-08-04 16:28:16 +02:00
2017-08-28 18:15:52 +02:00
if ( ! in_array ( $resource , array_keys ( $resources ))) {
2017-08-04 16:28:16 +02:00
return self :: error ( 'notFound' );
}
2017-08-28 18:15:52 +02:00
// Database
2017-09-01 18:13:25 +02:00
$database = Database :: getConnection ();
$dbo = $database ;
2017-08-04 16:28:16 +02:00
2017-09-04 16:04:45 +02:00
$database -> query ( 'START TRANSACTION' );
2017-08-28 18:15:52 +02:00
2017-09-04 16:04:45 +02:00
// Esecuzione delle operazioni
2017-08-04 16:28:16 +02:00
$filename = DOCROOT . '/modules/' . $resources [ $resource ] . '/api/' . $kind . '.php' ;
include $filename ;
2017-09-04 16:04:45 +02:00
$database -> query ( 'COMMIT' );
2017-08-28 18:15:52 +02:00
2017-08-04 16:28:16 +02:00
return self :: response ( $results );
}
2017-08-29 12:42:42 +02:00
/**
* Genera i contenuti di risposta nel caso si verifichi un errore .
*
* @ param string | int $error
*
* @ return string
*/
2017-08-04 16:28:16 +02:00
public static function error ( $error )
{
$keys = array_keys ( self :: $status );
2017-08-31 10:09:06 +02:00
$error = ( in_array ( $error , $keys )) ? $error : 'serverError' ;
$code = self :: $status [ $error ][ 'code' ];
2017-08-04 16:28:16 +02:00
2017-08-31 10:09:06 +02:00
http_response_code ( $code );
2017-08-29 12:42:42 +02:00
2017-08-04 16:28:16 +02:00
return self :: response ([
2017-08-31 10:09:06 +02:00
'status' => $code ,
2017-08-04 16:28:16 +02:00
]);
}
2017-08-29 12:42:42 +02:00
/**
* Restituisce l 'elenco delle risorse disponibili per l' API , suddivise per categoria .
*
* @ return array
*/
2017-08-04 16:28:16 +02:00
public static function getResources ()
{
if ( ! is_array ( self :: $resources )) {
$resources = [];
$operations = glob ( DOCROOT . '/modules/*/api/{retrieve,create,update,delete}.php' , GLOB_BRACE );
2017-09-04 16:04:45 +02:00
foreach ( $operations as $operation ) {
// Individua la tipologia e il modulo delle operazioni
$module = basename ( dirname ( dirname ( $operation )));
$kind = basename ( $operation , '.php' );
2017-08-28 18:15:52 +02:00
2017-09-04 16:04:45 +02:00
$resources [ $kind ] = ( array ) $resources [ $kind ];
2017-08-04 16:28:16 +02:00
2017-09-04 16:04:45 +02:00
// Controllo sulla presenza di eventuali personalizzazioni
$temp = str_replace ( '/api/' , '/custom/api/' , $operation );
$operation = file_exists ( $temp ) ? $temp : $operation ;
2017-08-04 16:28:16 +02:00
2017-09-04 16:04:45 +02:00
// Individuazione delle operazioni
$api = include $operation ;
$api = is_array ( $api ) ? array_unique ( $api ) : [];
2017-08-04 16:28:16 +02:00
2017-09-04 16:04:45 +02:00
$keys = array_keys ( $resources [ $kind ]);
2017-08-04 16:28:16 +02:00
2017-09-04 16:04:45 +02:00
// Registrazione delle operazioni individuate
$results = [];
foreach ( $api as $value ) {
$value .= in_array ( $value , $keys ) ? $module : '' ;
$results [ $value ] = $module ;
2017-08-04 16:28:16 +02:00
}
2017-09-04 16:04:45 +02:00
// Salvataggio delle operazioni
$resources [ $kind ] = array_merge ( $resources [ $kind ], $results );
2017-08-04 16:28:16 +02:00
}
self :: $resources = $resources ;
}
return self :: $resources ;
}
2017-08-29 12:42:42 +02:00
/**
* Formatta i contentuti della risposta secondo il formato JSON .
*
* @ param array $array
*
* @ return string
*/
2017-08-04 16:28:16 +02:00
public static function response ( $array )
{
2017-09-22 09:58:29 +02:00
if ( empty ( $array [ 'custom' ])) {
// Agiunta dello status di default
if ( empty ( $array [ 'status' ])) {
$array [ 'status' ] = self :: $status [ 'ok' ][ 'code' ];
}
2017-08-31 10:09:06 +02:00
2017-09-22 09:58:29 +02:00
// Aggiunta del messaggio in base allo status
if ( empty ( $array [ 'message' ])) {
$codes = array_column ( self :: $status , 'code' );
$messages = array_column ( self :: $status , 'message' );
2017-08-31 10:09:06 +02:00
2017-09-22 09:58:29 +02:00
$array [ 'message' ] = $messages [ array_search ( $array [ 'status' ], $codes )];
}
2017-08-31 10:09:06 +02:00
2017-09-22 09:58:29 +02:00
$flags = JSON_FORCE_OBJECT ;
// Beautify forzato dei risultati
if ( get ( 'beautify' ) !== null ) {
$flags |= JSON_PRETTY_PRINT ;
}
2017-08-04 16:28:16 +02:00
2017-09-22 09:58:29 +02:00
$result = json_encode ( $array , $flags );
} else {
$result = $array [ 'custom' ];
2017-08-04 16:28:16 +02:00
}
2017-09-22 09:58:29 +02:00
return $result ;
2017-08-04 16:28:16 +02:00
}
2017-08-29 12:42:42 +02:00
/**
* Restituisce l 'elenco degli stati dell' API .
*
* @ return array
*/
public static function getStatus ()
{
return self :: $status ;
}
/**
* Controlla se la richiesta effettuata è rivolta all ' API .
*
* @ return bool
*/
2017-08-04 16:28:16 +02:00
public static function isAPIRequest ()
{
2017-12-22 11:44:27 +01:00
return getURLPath () == slashes ( ROOTDIR . '/api/index.php' );
2017-08-04 16:28:16 +02:00
}
2017-08-29 12:42:42 +02:00
/**
* Restituisce i parametri specificati dalla richiesta .
*/
public static function getRequest ()
{
2017-09-04 16:04:45 +02:00
$request = [];
if ( self :: isAPIRequest ()) {
$request = ( array ) json_decode ( file_get_contents ( 'php://input' ), true );
2017-09-01 18:13:25 +02:00
2017-09-04 16:04:45 +02:00
// Fallback nel caso la richiesta sia effettuata da browser
if ( $_SERVER [ 'REQUEST_METHOD' ] == 'GET' && empty ( $request )) {
$request = Filter :: getGET ();
}
2017-09-01 18:13:25 +02:00
}
return $request ;
2017-08-29 12:42:42 +02:00
}
2017-08-31 10:09:06 +02:00
/**
* Controlla se il database è compatibile con l ' API .
*
* @ return bool
*/
public static function isCompatible ()
{
$database = Database :: getConnection ();
return version_compare ( $database -> getMySQLVersion (), '5.6.5' ) >= 0 ;
}
2017-08-04 16:28:16 +02:00
}