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' ],
]);
}
2018-08-09 15:33:01 +02:00
$response = [];
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
2018-05-03 17:29:10 +02:00
$values = isset ( $request [ 'filter' ]) ? ( array ) $request [ 'filter' ] : [];
foreach ( $values as $key => $value ) {
2017-09-04 16:04:45 +02:00
// 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
2018-05-03 17:29:10 +02:00
$values = isset ( $request [ 'order' ]) ? ( array ) $request [ 'order' ] : [];
foreach ( $values 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
2018-05-03 17:29:10 +02:00
$page = isset ( $request [ 'page' ]) ? ( int ) $request [ 'page' ] : 0 ;
2018-07-09 10:44:54 +02:00
$length = setting ( 'Lunghezza pagine per API' );
2017-09-01 18:13:25 +02:00
2018-08-10 17:14:09 +02:00
$dbo = $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
2018-07-27 12:17:17 +02:00
try {
if ( in_array ( $resource , array_keys ( $resources ))) {
2018-07-31 14:50:28 +02:00
// Inclusione funzioni del modulo
include_once App :: filepath ( DOCROOT . '/modules/' . $resources [ $resource ] . '|custom|' , 'modutil.php' );
2018-07-27 12:17:17 +02:00
// Esecuzione delle operazioni personalizzate
$filename = DOCROOT . '/modules/' . $resources [ $resource ] . '/api/' . $kind . '.php' ;
include $filename ;
} elseif (
! in_array ( $resource , explode ( ',' , setting ( 'Tabelle escluse per la sincronizzazione API automatica' )))
&& $database -> tableExists ( $resource )
) {
$table = $resource ;
// Individuazione della colonna AUTO_INCREMENT per l'ordinamento automatico
if ( empty ( $order )) {
$column = $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 ()));
if ( ! empty ( $column )) {
$order [] = $column [ 0 ][ 'COLUMN_NAME' ];
}
2018-05-03 17:29:10 +02:00
}
2018-07-27 12:17:17 +02:00
} else {
return self :: error ( 'notFound' );
2017-09-04 16:04:45 +02:00
}
2017-08-04 16:28:16 +02:00
2018-07-27 12:17:17 +02:00
// Generazione automatica delle query
2018-08-09 15:33:01 +02:00
if ( ! empty ( $table )) {
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
$query = $database -> select ( $table , $select , $where , $order , [], true );
2017-08-04 16:28:16 +02:00
}
2018-08-09 15:33:01 +02:00
$response [ 'records' ] = $database -> fetchArray ( $query . ' LIMIT ' . ( $page * $length ) . ', ' . $length , $parameters );
$count = $database -> fetchNum ( $query , $parameters );
$response [ 'total-count' ] = $count ;
$response [ 'pages' ] = ceil ( $count / $length );
2018-07-27 12:17:17 +02:00
} catch ( PDOException $e ) {
// Log dell'errore
$logger = logger ();
$logger -> addRecord ( \Monolog\Logger :: ERROR , $e );
return self :: error ( 'internalError' );
2017-08-04 16:28:16 +02:00
}
2018-08-09 15:33:01 +02:00
return self :: response ( $response );
2017-08-04 16:28:16 +02:00
}
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 ();
2018-08-09 15:33:01 +02:00
$response = [];
2018-08-01 15:32:23 +02:00
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' );
}
2018-08-28 15:39:51 +02:00
// Inclusione funzioni del modulo
include_once App :: filepath ( DOCROOT . '/modules/' . $resources [ $resource ] . '|custom|' , 'modutil.php' );
2017-08-28 18:15:52 +02:00
// Database
2018-08-10 17:14:09 +02:00
$dbo = $database = Database :: getConnection ();
2017-08-04 16:28:16 +02:00
2018-08-09 15:33:01 +02:00
$database -> beginTransaction ();
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 ;
2018-08-09 15:33:01 +02:00
$database -> commitTransaction ();
2017-08-28 18:15:52 +02:00
2018-08-09 15:33:01 +02:00
return self :: response ( $response );
2017-08-04 16:28:16 +02:00
}
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 = [];
2018-05-03 17:29:10 +02:00
// Ignore dei warning
$resource = '' ;
2018-02-09 15:10:06 +01:00
// File nativi
$files = glob ( DOCROOT . '/modules/*/api/{retrieve,create,update,delete}.php' , GLOB_BRACE );
// File personalizzati
$custom_files = glob ( DOCROOT . '/modules/*/custom/api/{retrieve,create,update,delete}.php' , GLOB_BRACE );
// Pulizia dei file nativi che sono stati personalizzati
foreach ( $custom_files as $key => $value ) {
$index = array_search ( str_replace ( 'custom/api/' , 'api/' , $value ), $files );
if ( $index !== false ) {
unset ( $files [ $index ]);
}
}
$operations = array_merge ( $files , $custom_files );
asort ( $operations );
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
2018-05-03 17:29:10 +02:00
$resources [ $kind ] = isset ( $resources [ $kind ]) ? ( array ) $resources [ $kind ] : [];
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 .
2018-08-20 13:09:27 +02:00
*
* @ param bool $raw
*
* @ return array
2017-08-29 12:42:42 +02:00
*/
2018-04-08 09:37:45 +02:00
public static function getRequest ( $raw = false )
2017-08-29 12:42:42 +02:00
{
2017-09-04 16:04:45 +02:00
$request = [];
if ( self :: isAPIRequest ()) {
2018-04-08 09:37:45 +02:00
$request = file_get_contents ( 'php://input' );
2017-09-01 18:13:25 +02:00
2018-04-08 09:37:45 +02:00
if ( empty ( $raw )) {
$request = ( array ) json_decode ( $request , true );
2018-07-28 17:57:01 +02:00
$request = Filter :: sanitize ( $request );
2018-04-08 09:37:45 +02:00
2018-08-20 13:09:27 +02:00
// Fallback per input standard vuoto (richiesta da browser o upload file)
if ( empty ( $request )) { // $_SERVER['REQUEST_METHOD'] == 'GET'
2018-04-08 09:37:45 +02:00
$request = Filter :: getGET ();
}
2018-05-03 17:29:10 +02:00
if ( empty ( $request [ 'token' ])) {
$request [ 'token' ] = '' ;
}
2017-09-04 16:04:45 +02:00
}
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
}