Commits

Mateusz Łopaciński committed 4a494d9

Added .htaccess, LICENSE, new View & some default views

Changed Router pattern api
Fixed Request, errors

Comments (0)

Files changed (11)

+RewriteEngine On
+
+
+RewriteRule ^(?:application|modules|system|Ray)\b.* index.php/$0 [L]
+
+RewriteCond %{REQUEST_FILENAME} !-d
+RewriteCond %{REQUEST_FILENAME} !-f
+RewriteRule ^(.*?)(\.html)?$ index.php/$1 [QSA,L]
+Copyright (c) 2012 robopuff.com
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
 namespace Ray;
 
 class Ray{
-    
+
     public  $config,
             $router,
             $view,
             $request,
             $response,
-            $errors,
             $dispatch;
-    
+
     public function __construct($config=array(), $preDispatch=null, $postDispatch=null){
         spl_autoload_register(array('Ray\Ray', 'autoload'));
 
         if ( @date_default_timezone_set(date_default_timezone_get()) === false ) {
             date_default_timezone_set('UTC');
         }
-        
+
         if(!defined('DS')){
             define('DS', DIRECTORY_SEPARATOR);
         }
-        
+
         if(!defined('ROOT')){
             define('ROOT', realpath(dirname(__FILE__)  . DS . '..' . DS ));
         }
-        
+
         $this->config = array_merge(array(
             'mode' => 'production',
             'templates' => ROOT . DS . 'template',
             'view' => 'Ray\View\Inline'
         ), $config);
-        
+
         if($preDispatch && is_callable($preDispatch)){
             $this->dispatch['pre'] = $preDispatch;
         }
-        
+
         if($postDispatch && is_callable($postDispatch)){
             $this->dispatch['post'] = $postDispatch;
         }
-        
+
         $this->router = new Router();
-        
+
         $this->response = new Response();
-        
+
         $this->request = new Request();
-        
+
         $this->response->responseCode(Response::OK);
     }
-    
+
     public function __call($method, $args){
-        
+
         $reflection = new \ReflectionObject($this->request);
-        
+
         $via = 'METHOD_' . strtoupper($method);
-        
+
         if($reflection->hasConstant($via)){
             return $this->mapRoute($reflection->getConstant($via), $args);
         }else{
             throw new Exception\Call('What the heck is that!? <i>__call('.$method.', '.var_export($args, true).')</i>');
         }
     }
-    
+
     public function __destruct(){
-        
+
         if($this->router){
             if(isset($this->dispatch['pre'])){
                 call_user_func($this->dispatch['pre']);
             }
-            
+
             $return = $this->router->proccess($this->request->getRequest());
-            
+
             if($return){
-                $view = new $this->config['view']($return, $this->config);
+                $viewClass = isset($return['view']) && $return['view'] ? $return['view'] : $this->config['view'];
+                $view = new $viewClass($return['callback'], $this->config);
                 $send = $view->render();
-            }elseif(isset($this->error[Response::NOT_FOUND])){
+            }elseif($this->router->hasRoute(Request::METHOD_GET, '/error/404')){
                 $response = Response::NOT_FOUND;
-                $send =  $this->error[Response::NOT_FOUND];
+                
+                $request = $this->request->getRequest();
+                
+                $request->method = Request::METHOD_GET;
+                
+                $request->uri = '/error/404';
+                
+                $error = $this->router->proccess($request);
+                
+                $viewClass = isset($error['view']) && $error['view'] ? $error['view'] : $this->config['view'];
+                
+                $view = new $viewClass($error['callback'], $this->config);
+                
+                $send =  $view->render();
+                
             }else{
                 $response = Response::NOT_FOUND;
                 $send = Response::NOT_FOUND;
             }
-            
+
             if(isset($response)){
                 $this->response->responseCode($response);
             }
-            
+
             if(isset($this->dispatch['post'])){
                 call_user_func($this->dispatch['post']);
             }
-            
+
             $this->response->send($send);
         }
     }
-    
+
     private function mapRoute($method, $args){
-        $pattern = $args[0];
-        $callable = end($args);
-        return $this->router->map($method, $pattern, $callable);
+        if(count($args) >= 2){
+            $pattern = $args[0];
+            $callable = $args[1];
+            $view = isset($args[2]) ? $args[2] : $this->config['view'];
+
+            return $this->router->map($method, $pattern, $callable, $view);
+        }
+
+        return false;
     }
-    
+
     public function map(){
         $args = func_get_args();
         return $this->mapRoute($args);
     }
-    
-    public function error($code, $callable){
-        if(is_callable($callable)){
-            $this->errors[$code] = $callable;
-        }else{
-            throw new Exception\Call('Hey, man $callable isnt callable');
-        }
+
+    public function error($code, $callable, $view=null){
+        $this->get('/error/'.$code, $callable, $view);
     }
-    
+
     /**
      * Autoload
      */
     public static function autoload( $class ) {
 
         $file = ROOT . DIRECTORY_SEPARATOR . str_replace('\\', DIRECTORY_SEPARATOR, $class) . '.php';
-        
+
         if ( file_exists($file) ) {
             require $file;
         }
     }
-}
+}
 namespace Ray;
 
 class Request{
-    
+
     const METHOD_HEAD = 'HEAD';
-    
+
     const METHOD_GET = 'GET';
-    
+
     const METHOD_POST = 'POST';
-    
+
     const METHOD_PUT = 'PUT';
-    
+
     const METHOD_DELETE = 'DELETE';
-    
+
     const METHOD_PATCH = 'PATCH';
-    
+
     const METHOD_OPTIONS = 'OPTIONS';
-    
+
     protected $additionalHeaders = array(
         'content-type', 'content-length', 'php-auth-user',
         'php-auth-pw', 'auth-type', 'x-requested-with'
     );
-    
+
     private $request;
-    
+
     public function __construct(){
-        
+
         $headers = array();
         foreach ( $_SERVER as $key => $value ) {
             $key = $this->convertHeader($key);
                 $headers[$name] = $value;
             }
         }
-        
+
         $this->request = (object) array(
             'host'    => $_SERVER['HTTP_HOST'],
             'method'  => isset($_SERVER['REQUEST_METHOD']) ? $_SERVER['REQUEST_METHOD'] : self::METHOD_GET,
             'headers' => $headers,
-            'uri'     => $_SERVER['PATH_INFO']
+            'uri'     => $this->getUri()
         );
-        
-    }
 
-    protected function convertHeader( $name ) {
-        return str_replace('_', '-', strtolower($name));
     }
-    
+
     public function isAjax() {
         return ( isset($this->request->headers['X_REQUESTED_WITH']) &&
                 $this->request->headers['X_REQUESTED_WITH'] === 'XMLHttpRequest' );
     }
-    
+
     public function getRequest(){
         return $this->request;
     }
-    
+
+    protected function convertHeader( $name ) {
+        return str_replace('_', '-', strtolower($name));
+    }
+
+    private function getUri(){
+        $scheme = ( empty($_SERVER['HTTPS']) || $_SERVER['HTTPS'] === 'off' ) ? 'http' : 'https';
+
+        $uri = '/';
+
+        if ( !empty($_SERVER['PATH_INFO']) ) {
+            $uri = $_SERVER['PATH_INFO'];
+        } else {
+            if ( isset($_SERVER['REQUEST_URI']) ) {
+                $uri = parse_url($scheme . '://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'], PHP_URL_PATH);
+            } else if ( isset($_SERVER['PHP_SELF']) ) {
+                $uri = $_SERVER['PHP_SELF'];
+            } else {
+                throw new RuntimeException('Unable to detect request URI');
+            }
+        }
+
+        return $uri;
+    }
+
 }
 class Router{
 
     private $routes = array();
-    
-    public function map($method, $pattern, $callable){
-        $route = $this->routes[$method][$pattern] =  new Router\Route($pattern, $callable);
-        
+
+    public function map($method, $pattern, $callable, $view=null){
+
+        if(is_array($pattern)){
+            $return = array();
+
+            foreach($pattern as $p){
+                $return[] = $this->map($method, $p, $callable, $view);
+            }
+
+            return $return;
+        }
+
+        $route = $this->routes[$method][$pattern] =  new Router\Route($pattern, $callable, $view);
+
         return $route;
     }
+
+    public function hasRoute($method, $pattern){
+        return isset($this->routes[$method]) && isset($this->routes[$method][$pattern]);
+    }
     
     public function proccess($request){
 
         if(isset($this->routes[$request->method])){
             $active = false;
-            
+
             foreach($this->routes[$request->method] as $route){
                 if($route->check($request->uri) === true){
                     $active = $route;
                     break;
                 }
             }
-            
+
             if($active){
                 return $active->proccess();
             }
         }
-        
+
         return false;
     }
-    
-}
+
+}

Ray/Router/Route.php

 use Ray;
 
 class Route{
-    
+
     private $pattern;
-    
+
     private $callable;
-    
+
+    private $view;
+
     private $params;
-    
+
     private $conditions = array();
-    
-    public function __construct($pattern, $callable){
+
+    public function __construct($pattern, $callable, $view=null){
         $this->pattern = $pattern;
         $this->callable = $callable;
+        $this->view = $view;
     }
-    
+
     public function check($call){
-        
+
         preg_match_all('@:([\w]+)@', $this->pattern, $params, PREG_PATTERN_ORDER);
         $params = current($params);
-        
+
         $regex = preg_replace_callback('@:[\w]+@', array($this, 'getRegex'), $this->pattern);
-        
+
         if ( substr($this->pattern, -1) === '/' ) {
             $regex = $regex . '?';
         }
-        
+
         if(preg_match('@^' . $regex . '$@', $call, $values)){
             array_shift($values);
             foreach($params as $k=>$v){
                     $this->params[$val] = urldecode($values[$val]);
                 }
             }
-            
+
             return true;
         }
-        
+
         return false;
     }
-    
+
     public function conditions(array $conditions){
         $this->conditions = $conditions;
     }
-    
+
     public function proccess(){
+
         if(is_array($this->params)){
-            return call_user_func_array($this->callable, $this->params);
+            $return = call_user_func_array($this->callable, $this->params);
+        }else{
+            $return = call_user_func($this->callable);
         }
-        
-        return call_user_func($this->callable);
+
+        return array(
+            'view' => $this->view,
+            'callback' => $return
+        );
     }
-    
+
     protected function getRegex( $matches ) {
         $key = substr(current($matches),1);
-        
-        $condition = isset($this->conditions[$key]) ? $this->conditions[$key] : 
+
+        $condition = isset($this->conditions[$key]) ? $this->conditions[$key] :
                      '.*?';
-        
+
         return '(?P<'.$key.'>' . $condition . ')';
     }
-    
-}
+
+}

Ray/View/Mustache.php

 
 class Mustache implements Ray\View{
     
-    private $options = array(
+    protected $options = array(
         'ext' => '.mustache',
         'layout' => 'default',
         'actionDir' => 'action',
-        'action' => 'error404',
+        'action' => '404',
         'params' => array()
     );
     
-    private $config;
+    protected $config;
     
-    private $instance;
+    protected $instance;
     
     public function __construct(array $response, $config){
         
         
     }
     
-    public function render(){
+    public function render($layoutAsText=null, $actionAsText=null){
         
         $layout = $this->config['templates'] . DS . $this->options['layout'] . $this->options['ext'];
         
         $action = $this->config['templates'] . DS . $this->options['actionDir'] . DS . $this->options['action'] . $this->options['ext'];
         
-        if(file_exists($layout)){
-            $template = file_get_contents($layout);
+        if($layoutAsText || file_exists($layout)){
+            $template = ($layoutAsText) ? $layoutAsText : file_get_contents($layout);
             
             $params = is_array($this->options['params']) ? $this->options['params'] : array();
             
-            if(file_exists($action)){
+            $params['bodyID'] = $this->options['action'];
+            
+            if($actionAsText || file_exists($action)){
                 $partial = array(
-                    'action' => file_get_contents($action)
+                    'action' => ($actionAsText) ? $actionAsText : file_get_contents($action)
                 );
                 
                 return $this->instance->render($template, $params, $partial);

Ray/View/Mustache/OneFile.php

+<?php
+
+namespace Ray\View\Mustache;
+
+use Ray;
+
+class OneFile extends Ray\View\Mustache implements Ray\View {
+    
+    protected $options = array(
+        'ext' => '',
+        'layout' => 'layout',
+        'actionDir' => '',
+        'action' => '404',
+        'params' => array()
+    );
+    
+    private $preg = "/^@@\s*(.*\S)\s*$/sm";
+    
+    public function render(){
+        
+        $file = $_SERVER['SCRIPT_FILENAME'];
+        
+        $halt = '__halt_compiler();';
+        
+        $fp = fopen($file, 'r');
+        
+        if(defined('__COMPILER_HALT_OFFSET__')){
+            fseek($fp, __COMPILER_HALT_OFFSET__);
+        }else{
+            $read = strtolower(fread($fp, filesize($file)));
+            
+            $pos = strpos($read, $halt);
+            
+            if($pos > 0){
+                $newLine = strpos($read, "\n", $pos);
+                
+                fseek($fp, $newLine);
+            }
+        }
+        
+        $lines = explode("\n", stream_get_contents($fp));
+        
+        $template = false;
+        
+        $templates = array();
+        
+        foreach($lines as $line){            
+            if(strlen(trim($line))>0){
+                if(preg_match($this->preg, $line, $match)){
+                    $template = $match[1];
+                    $templates[$template] = '';
+                }elseif($template){
+                    $templates[$template] .= "\n" . $line;
+                }
+            }
+        };
+        
+        if(!isset($templates[$this->options['layout']])){
+            throw new Ray\Exception\View\Mustache('Layout template not found - Search: '.$this->options['layout']);
+            return;
+        }
+        
+        if(!isset($templates[$this->options['action']])){
+            throw new Ray\Exception\View\Mustache('Acton template not found - Search: '.$this->options['action']);
+            return;
+        }
+        
+        $layout = $templates[$this->options['layout']];
+        
+        $action = $templates[$this->options['action']];
+        
+        return parent::render($layout, $action);
+    }
+
+}
 error_reporting(E_ALL | E_STRICT);
 
 require_once 'Ray/Ray.php';
-    
+
 try{
-    
+
     $app = new Ray\Ray(array(
-        'view' => 'Ray\View\Mustache'
+        'view' => 'Ray\View\Mustache\OneFile'
     ));
-    
+
     $app->error(404, function(){
         return array(
             'action' => '404'
         );
     });
-    
-    $app->get('/', function(){
+
+    $app->get(array('/','/readme'), function(){
         return array(
             'action' => 'index'
         );
     });
-    
-    $app->get('/post/:id/', function($id=7){
-        return array(
-            'action' => 'post'
-        );
-    });
-    
+
 }catch(\Exception $e){
-    echo '<pre>' . $e . '</pre>';
+    var_dump($e);
 }
+
+__halt_compiler(); //?>
+
+@@ layout
+<html>
+    <head>
+        <title>Ray :: work in one file!</title>
+    </head>
+    <body>
+        <h1>Ray works great!</h1>
+        {{>action}}
+    </body>
+</html>
+
+@@ 404
+<h2>Error 404!</h2>
+
+@@ index
+<h2>Index view!</h2>

template/action/404.mustache

+<h2>Error 404</h2>

template/action/404.php

+<h2>Error 404</h2>
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.