openrat-cms

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README

API.class.php (7275B)


      1 <?php
      2 
      3 namespace cms\api;
      4 
      5 use BadMethodCallException;
      6 use cms\action\RequestParams;
      7 use cms\base\Startup;
      8 use cms\Dispatcher;
      9 use Exception;
     10 use util\Http;
     11 use logger\Logger;
     12 use \util\exception\ObjectNotFoundException;
     13 use util\exception\UIException;
     14 use util\exception\SecurityException;
     15 use util\json\JSON;
     16 use util\Session;
     17 use util\XML;
     18 use util\YAML;
     19 
     20 /**
     21  * Entrypoint for all API requests.
     22  */
     23 class API
     24 {
     25 	const OUTPUT_PHPARRAY     = 1;
     26 	const OUTPUT_PHPSERIALIZE = 2;
     27 	const OUTPUT_JSON         = 3;
     28 	const OUTPUT_XML          = 4;
     29 	const OUTPUT_YAML         = 5;
     30 	const OUTPUT_HTML         = 6;
     31 	const OUTPUT_PLAIN        = 7;
     32 
     33 
     34 	/**
     35      * Führt einen API-Request durch.
     36      */
     37     public static function execute()
     38     {
     39     	$createDataWithError = function( $status, $message, $cause ) {
     40 
     41 			Logger::warn($cause);
     42 			API::sendHTTPStatus($status, $message);
     43 
     44 			$data = [
     45 				'status'  => $status,
     46 				'message' => $message
     47 			];
     48 
     49 			// Traces only in DEVELOPMENT mode
     50 			// for security reasons, because traces may contain sensitive information.
     51 			if (!defined('DEVELOPMENT') || DEVELOPMENT)
     52 				$data['cause'] = API::exceptionToArray($cause);
     53 
     54 			return $data;
     55 		};
     56 
     57         try {
     58             $request = new RequestParams();
     59 
     60             $dispatcher = new Dispatcher();
     61 
     62             $dispatcher->request = $request;
     63 
     64             $data = $dispatcher->doAction();
     65 
     66         } catch (BadMethodCallException $e) {
     67             $data = $createDataWithError( 204, 'Method not found'  , $e );
     68         } catch (ObjectNotFoundException $e) {
     69 			$data = $createDataWithError( 204, 'Object not found'  , $e );
     70         } catch (UIException $e) {
     71 			$data = $createDataWithError( 500, 'Internal CMS Error', $e );
     72         } catch (SecurityException $e) {
     73 			$data = $createDataWithError( 403, 'Forbidden'          , $e );
     74         } catch (Exception $e) {
     75 			$data = $createDataWithError( 500, 'Internal Server Error', $e );
     76         }
     77 
     78 
     79         if ( Logger::isTraceEnabled() )
     80             Logger::trace('Output' . "\n" . print_r($data, true));
     81 
     82         // Weitere Variablen anreichern.
     83         $data['session'] = ['name' => session_name(), 'id' => session_id(), 'token' => Session::token()];
     84         $data['version'] = Startup::VERSION;
     85         $data['api'    ] = Startup::API_LEVEL;
     86 
     87 
     88         switch (API::discoverOutputType()) {
     89 
     90             case self::OUTPUT_PHPARRAY:
     91                 header('Content-Type: application/php-array; charset=UTF-8');
     92                 $output = print_r($data, true);
     93                 break;
     94 
     95 			case self::OUTPUT_PLAIN:
     96 				header('Content-Type: text/plain; charset=UTF-8');
     97 				$output = print_r($data, true);
     98 				break;
     99 
    100 			case self::OUTPUT_PHPSERIALIZE:
    101                 header('Content-Type: application/php-serialized; charset=UTF-8');
    102                 $output = serialize($data);
    103                 break;
    104 
    105             case self::OUTPUT_JSON:
    106                 header('Content-Type: application/json; charset=UTF-8');
    107 	            $output = JSON::encode($data);
    108                 break;
    109 
    110             case self::OUTPUT_XML:
    111                 $xml = new XML();
    112                 $xml->root = 'server'; // Name des XML-root-Elementes
    113                 header('Content-Type: application/xml; charset=UTF-8');
    114                 $output = $xml->encode($data);
    115                 break;
    116 
    117             case self::OUTPUT_HTML:
    118                 header('Content-Type: text/html; charset=UTF-8');
    119                 $output  = '<html><body><h1>API response:</h1><hr /><pre>';
    120                 $output .= print_r($data,true);
    121                 $output .= '</pre></body></html>';
    122                 break;
    123 
    124             case self::OUTPUT_YAML:
    125                 header('Content-Type: application/yaml; charset=UTF-8');
    126                 $output = YAML::dump($data);
    127                 break;
    128         }
    129 
    130 
    131         if (!headers_sent())
    132             // HTTP Spec:
    133             // "Applications SHOULD use this field to indicate the transfer-length of the
    134         	//  message-body, unless this is prohibited by the rules in section 4.4."
    135             //
    136             // And the overhead of 'Transfer-Encoding: chunked' is eliminated...
    137             header('Content-Length: ' . strlen($output));
    138 
    139         echo $output;
    140     }
    141 
    142     /**
    143      * Discovering the output-type for this API-request
    144      *
    145      * @return int constant of self::CMS_API_OUTPUT_*
    146      */
    147     private static function discoverOutputType()
    148     {
    149         $types = Http::getAccept();
    150 
    151         $reqOutput = strtolower(@$_REQUEST['output']);
    152 
    153         // First check: The output parameter has precedence over HTTP headers
    154         if ( $reqOutput == 'php-array')
    155             return self::OUTPUT_PHPARRAY;
    156 
    157         if ( $reqOutput == 'php')
    158             return self::OUTPUT_PHPSERIALIZE;
    159 
    160         if ( $reqOutput == 'json')
    161             return self::OUTPUT_JSON;
    162 
    163         if ( $reqOutput == 'xml')
    164             return self::OUTPUT_XML;
    165 
    166         if ( $reqOutput == 'yaml')
    167             return self::OUTPUT_YAML;
    168 
    169         // Lets check the HTTP request headers
    170         if (in_array('application/php-array', $types) )
    171             return self::OUTPUT_PHPARRAY;
    172 
    173         if (in_array('application/php-serialized', $types) )
    174             return self::OUTPUT_PHPSERIALIZE;
    175 
    176         if (in_array('application/json', $types) )
    177             return self::OUTPUT_JSON;
    178 
    179         if (in_array('application/xml', $types) )
    180             return self::OUTPUT_XML;
    181 
    182         if (in_array('application/yaml', $types) )
    183             return self::OUTPUT_YAML;
    184 
    185         if (in_array('text/html', $types))
    186             return self::OUTPUT_HTML;  // normally an ordinary browser.
    187 
    188         return self::OUTPUT_PLAIN; // Fallback
    189     }
    190 
    191     /**
    192      * @param $status int HTTP-Status
    193      * @param $text string Statustext
    194      */
    195     private static function sendHTTPStatus($status, $text)
    196     {
    197         if (headers_sent()) {
    198             //echo "$status $text";
    199             ; // There is nothing we can do. Every output would destroy the JSON, XML, whatever.
    200         } else {
    201             header('HTTP/1.0 ' . intval($status) . ' ' . $text);
    202         }
    203     }
    204 
    205     /**
    206      * Converting an exception to an array.
    207      *
    208      * This will contain all exceptions out of the exception chain.
    209      *
    210      * @param $e Exception
    211      */
    212     private static function exceptionToArray($e)
    213     {
    214         $data = array(
    215             'error'=>get_class($e),
    216             'description'=>$e->getMessage(),
    217             'code'=>$e->getCode(),
    218 
    219             'trace'=>array_merge( array( array(
    220                 'file'=>$e->getFile(),
    221                 'line'=>$e->getLine(),
    222                 'function'=>'',
    223                 'class'   => ''
    224                 )), API::removeArgsFromTrace($e->getTrace()))
    225         );
    226 
    227         // the cause of the exception is another exception.
    228         if   ( $e->getPrevious() )
    229             $data['cause'] = API::exceptionToArray($e->getPrevious() );
    230 
    231         return $data;
    232     }
    233 
    234 
    235     /**
    236      * Removing the call argument from the trace.
    237      *
    238      * This is because of security reasons. The arguments could be an information leak.
    239      *
    240      * @param $trace array
    241      * @return array
    242      */
    243     private static function removeArgsFromTrace($trace)
    244     {
    245         foreach( $trace as &$t )
    246         {
    247             unset($t['args']);
    248         }
    249 
    250         return $trace;
    251     }
    252 }