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 }