rest-proxy.php

Simple proxy for RESTful APIs
git clone git://git.finwo.net/app/rest-proxy.php
Log | Files | Refs | README

Application.php (9600B)


      1 <?php
      2 
      3 namespace Finwo\Framework;
      4 
      5 use Finwo\PropertyAccessor\PropertyAccessor;
      6 use Invoker\Invoker;
      7 use OAuth2\Exception;
      8 
      9 class Application
     10 {
     11     /**
     12      * @var ParameterBag
     13      */
     14     protected $container;
     15 
     16     /**
     17      * @var PropertyAccessor
     18      */
     19     protected $propertyAccessor;
     20 
     21     /**
     22      * @var Application
     23      */
     24     protected $app;
     25 
     26     /**
     27      * @return PropertyAccessor
     28      */
     29     protected function getPropertyAccessor()
     30     {
     31         if(is_null($this->propertyAccessor)) {
     32             $this->propertyAccessor = new PropertyAccessor();
     33         }
     34         return $this->propertyAccessor;
     35     }
     36 
     37     public function __construct( ParameterBag $container = null )
     38     {
     39         // Don't do anything if we're an actual application
     40         if (!is_null($container)) {
     41             $this->container = $container;
     42             return;
     43         }
     44 
     45         // Load basic container
     46         $this->container = new ParameterBag(array(
     47 
     48             // Relevant about the application
     49             'document_root' => $_SERVER['DOCUMENT_ROOT'],
     50 
     51             // Relevant about the current request
     52             'method' => $_SERVER['REQUEST_METHOD']
     53         ));
     54 
     55         // Very useful extensions
     56         $accessor = $this->getPropertyAccessor();
     57         $loader = new \Spyc();
     58 
     59         // Empty array, because we want to be fancy later on
     60         $config = array();
     61 
     62         // Load configuration files
     63         $files = glob($this->container->get('document_root') . '/config/*.yml');
     64         foreach ($files as $file) {
     65             $accessor->mergeArrays(
     66                 $config,
     67                 $loader->loadFile($file)
     68             );
     69         }
     70 
     71         $this->container->set('config', $config);
     72 
     73         // Transform routes into route objects, for easy usage
     74         $routes = array();
     75         foreach ($this->container->get('config.routes') as $name => $route) {
     76             $routes[] = $route = new Route($name, array_merge(
     77                 $_SERVER,
     78                 $_REQUEST,
     79                 $route
     80             ));
     81         }
     82         $this->container->set('config.routes', $routes);
     83 
     84         // Check if we can do something useful with the application
     85         $app = $this->getApplicationObject( $this->container->get('config.application') );
     86         if ($app === false) {
     87             throw new \Exception('Application does not exist!');
     88         }
     89 
     90         // Save the app for later use
     91         $this->app = $app;
     92     }
     93 
     94     protected function getApplicationObject( $name )
     95     {
     96         // Try name directly
     97         if (class_exists($name)) {
     98             return new $name( $this->container );
     99         }
    100 
    101         // Try name with ending "application"
    102         $tryname = $name . '\\Application';
    103         if (class_exists($tryname)) {
    104             return new $tryname( $this->container );
    105         }
    106 
    107         // Try name with double ending
    108         $tryname = explode('\\', $name);
    109         $last = array_pop($tryname);
    110         array_push($tryname, $last);
    111         array_push($tryname, $last);
    112         $tryname = implode('\\', $tryname);
    113 
    114         if (class_exists($tryname)) {
    115             return new $tryname( $this->container );
    116         }
    117 
    118         return false;
    119     }
    120 
    121     public function launch()
    122     {
    123         // If we have a child, launch that instead
    124         if( $this->app instanceof Application ) {
    125             return $this->app->launch();
    126         }
    127 
    128         // Check if we match any known routes
    129         /** @var Route $route */
    130         $route = @array_shift(array_filter($this->container->get('config.routes'), function(Route $route) {
    131             return $route->match();
    132         }));
    133 
    134         // Construct a callable
    135         $controller = '';
    136         $method     = '';
    137         if (!is_null($route)) {
    138             // Fetch callable for the route
    139             list($controller, $method) = $this->routeToCallable($route);
    140         } else {
    141             // Find something based upon the request uri
    142             die('Not Implemented');
    143         }
    144 
    145         // Create the controller object
    146         $controller = new $controller($this->container);
    147 
    148         // Call the function
    149         $invoker = new Invoker();
    150         $answer = $invoker->call(array($controller, $method), array_merge(
    151             $route->getParameters(),
    152             array(
    153                 'route'     => $route,
    154                 'container' => $this->container,
    155                 'query'     => $route->getParameters()
    156             )
    157         ));
    158 
    159         switch($route->getType()) {
    160             case 'rest':
    161                 // Must transform data
    162 
    163                 // Check how to transform
    164                 $format = null;
    165                 if (isset($route->getParameters()['format'])) {
    166                     $format = $route->getParameters()['format'];
    167                 } else {
    168                     preg_match('/\.(?<format>[a-z]+)\?/i', $route->getRequestUri(), $matches);
    169                     if (isset($matches['format'])) {
    170                         $format = $matches['format'];
    171                     }
    172                 }
    173                 if (is_null($format)) {
    174                     throw new \Exception('Target format not given');
    175                 }
    176 
    177                 switch(strtolower($format)) {
    178                     case 'json':
    179                         header('Content-Type: application/json');
    180                         die(json_encode($answer));
    181                     case 'xml':
    182                         header('Content-Type: application/xml');
    183                         $transformer = new XmlTransformer();
    184                         die($transformer->objToXML($answer));
    185                     default:
    186                         throw new \Exception('Target format not supported');
    187                         die();
    188                 }
    189 
    190                 die('transform');
    191             default:
    192                 // Must return a view
    193                 die('Not implemented yet');
    194                 break;
    195         }
    196     }
    197 
    198     protected function routeToCallable( Route $route )
    199     {
    200         @list( $namespace, $controller, $method ) = explode(':', $route->getController());
    201 
    202         // This might introduce bugs we don't want
    203 //        // Add prefix to the uri if used
    204         $parsedPath = $route->getParsedUri()['path'];
    205 //        if (strlen($prefix=$route->getPrefix())) {
    206 //            $parsedPath = $prefix . $parsedPath;
    207 //        }
    208 
    209         // Pre-fetch uri, we might need to work with it
    210         $uri = explode('/', trim($parsedPath, '/'));
    211 
    212         // Auto-detect namespace if needed
    213         if (is_null($namespace) || !strlen($namespace)) {
    214             $ownClass = explode('\\', get_class($this));
    215             array_pop($ownClass);
    216             $namespace = implode('\\', $ownClass);
    217         }
    218 
    219         // Add "Controller" to the namespace
    220         $namespace .= '\\Controller';
    221 
    222         // Check if the controller exists
    223         if(!class_exists(sprintf("%s\\%sController", $namespace, $controller))) {
    224 
    225             // Try to fetch from uri
    226             if(count($uri) && class_exists(sprintf("%s\\%sController", $namespace, ucfirst($uri[0])))) {
    227                 $controller = ucfirst(array_shift($uri));
    228             }
    229 
    230             // Try the defaultcontroller
    231             if(!strlen($controller) && class_exists(sprintf("%s\\DefaultController", $namespace))) {
    232                 $controller = 'Default';
    233             }
    234 
    235             if(!strlen($controller)) {
    236                 // Too bad, no controller found
    237                 throw new \Exception('No proper controller found');
    238             }
    239         }
    240 
    241         // Merge fields into full controller class name
    242         $controller = sprintf("%s\\%sController", $namespace, $controller);
    243 
    244         // Fix method if needed
    245         if( is_null($method) || !strlen($method) || !method_exists($controller, $method) ) {
    246             $parts = $uri;
    247 
    248             // Check if appending 'action' does the trick
    249             if (method_exists($controller, $method . 'Action')) {
    250                 $method .= 'Action';
    251             } else {
    252                 // Make sure the rest of the code catches this
    253                 $method = null;
    254             }
    255 
    256             if (is_null($method) || !strlen($method)) {
    257                 while(count($parts)) {
    258                     if(method_exists($controller, ($camelized = $this->camelizedArray($parts)) . 'Action')) {
    259                         $method = $camelized . 'Action';
    260                         break;
    261                     }
    262                     array_pop($parts);
    263                 }
    264 
    265             }
    266 
    267             // Try request method
    268             if (is_null($method) || !strlen($method)) {
    269                 if (method_exists($controller, ($m=strtolower($route->getRequestMethod())) . 'Action')) {
    270                     $method = $m . 'Action';
    271                 }
    272             }
    273 
    274             // Try index method
    275             if (is_null($method) || !strlen($method)) {
    276                 if (method_exists($controller, 'indexAction')) {
    277                     $method = 'indexAction';
    278                 }
    279             }
    280 
    281             // Throw exception, we don't have a function
    282             if (is_null($method) || !strlen($method)) {
    283                 throw new \Exception('No method for the route found');
    284             }
    285         }
    286 
    287         // Fetch how to call the method (parameters etc)
    288         $parameters = array();
    289 
    290         // We're here, that means we have all the requirements
    291         $chosenController = new $controller($this->container);
    292 
    293         return array($controller,$method);
    294     }
    295 
    296     protected function camelizedArray($input) {
    297         $output = lcfirst(array_shift($input));
    298 
    299         foreach($input as $str) {
    300             $output .= ucfirst($str);
    301         }
    302 
    303         return $output;
    304     }
    305 }