Jump to content
×
×
  • Create New...

Single Framework


v4vx
 Share

Recommended Posts

Centre de Téléchargement

Hidden Content

    Give reaction to this post to see the hidden content.
( Interne )

Aujourd'hui, me suis donné un défie : faire un framework le plus petit et complet possible. Impossible me direz vous ? Petit est l'opposé de complet ?

 

C'est pour ça que ce framework est quelque peu singulier... Il permet de faire ce que des gros frameworks permettent de faire en... 1 fichier et 213 lignes ! (Ça permettra de concurrencer KowFramework, comme Diamond n'est pas près d'arriver)

Pour arriver à une telle prouesse, je n'ai gardé que l'essentiel. Il est composé de 10 classes, dont 3 abstraites (qui ne font rien en elle-même), qui permettent de faire :

 

  • Organisation du pattern MVC
  • Gestion des Active Record (utilisation de PDO)
  • Un mini router du style index.php/controller/method/arg1/...
  • Une gestion de l'output avec layout
  • Un système de cache (uniquement file... faut pas trop en demander )
  • Un registre complet pour variables
  • Un loader qui permet de charger des lib, ou les modèles, ect...

 

Et tout ça, avec un temps de génération de 2,2ms avec 120Ko de mémoire, sur une page de news (par SQL bien sur). Ce framework est codé pour php 5.1 et ultérieur (bref... compatible partout), il fait 10Ko avec ses 9962 caractères. Il sera parfait pour coder des CMS qui n'ont pas besoin des fonctions d'un gros framework, tout en gardant un certain minimum, ainsi que la puissance.

 

GitHub :

Hidden Content

    Give reaction to this post to see the hidden content.

 

Révélation


<?php
if(!defined('BASE')){define('BASE', dirname(__FILE__).'/../');}
/**
 * @property-read Single_Loader $loader
 * @property Single_Output $output
 * @property-read Single_Cache $cache
 */
class Single{
    private static $instance=null;
    private $vars=array();
    public $config=array();
    private function __construct(){
        self::$instance=$this;
        $this->loader=new Single_Loader();
    }
    public function __get($name){return isset($this->vars[$name])?$this->vars[$name]:null;}
    public function __set($name,$value){$this->vars[$name]=$value;}
    /** @return Single */
    public static function instance(){
        if(!self::$instance)new Single();
        return self::$instance;
    }
    public function loadWebApp(){
        $params=explode('/',isset($_SERVER['PATH_INFO'])?substr($_SERVER['PATH_INFO'],1):'');
        $this->controller=trim(array_shift($params));
        if($this->controller==='')$this->controller='home';
        $this->method=trim(array_shift($params));
        if($this->method==='')$this->method='index';
        $this->call_params=(array)$params;
        $this->loader->controller(ucfirst(trim(strtolower($this->controller))).'Controller',trim(strtolower($this->method)).'Action',$this->call_params);
    }
    public function __destruct(){$this->output->flush();}
}
abstract class Single_Component{
    private $_instance;
    public function __construct(){$this->_instance=Single::instance();}
    public function __get($name){return $this->_instance->$name;}
    public function __set($name, $value){return $this->_instance->$name=$value;}
}
class Single_Loader extends Single_Component{
    public function __construct(){
        parent::__construct();
        $this->output=new Single_Output();
        $this->cache=new Single_Cache();
    }
    public function load($class,$file,array $param=array()){
        if(!class_exists($class))require $file;
        if($this->$class!==null)throw new Exception('Class "'.$class.'" already loaded !');
        if($param===array())return $this->$class=new $class();
        $r=new ReflectionClass($class);
        return $this->$class=$r->newInstanceArgs($param);
    }
    public function alias($var,$alias){
        if($this->$var===null){
            throw new Exception('The class "'.$var.'" do not exists !');
            exit;
        }
        if($this->$alias!==null){
            throw new Exception('The alias "'.$alias.'" is already used !');
            exit;
        }
        $this->$alias=$this->$var;
    }
    public function controller($name,$method=null,array $param=array()){
        $file=BASE.(empty($this->config['controllers_path'])?'controllers':$this->config['controllers_path']).DIRECTORY_SEPARATOR.$name.'.php';
        if(!file_exists($file))$this->output->error(404,'Cannot find the controller !', true);
        require $file;
        if(class_exists($name)){
            $controller=new $name;
            if(!method_exists($controller, $method)) $this->output->error(404,'Cannot find the method "'.$method.'" !',true);
            echo call_user_func_array(array($controller,$method),$param);
        }elseif($method!==null){
            if(!is_callable($method)) $this->output->error(404,'Cannot find the function "'.$method.'" !',true);
            echo call_user_func_array($method, $param);
        }
    }
    public function model($name){
        $file=BASE.(empty($this->config['models_path'])?'models':$this->config['models_path']).DIRECTORY_SEPARATOR.$name.'.php';
        if(!file_exists($file))throw new Exception('The model "'.$name.'" do not exists !');
        require $file;
        if(!class_exists($name))return;
        return $this->$name=new $name;
    }
}
class Single_Output{
    private $contents='',$cache=false,$vars=array();
    public $layout=null;
    public function __construct(){ob_start();}
    public function __get($name){return isset($this->vars[$name])?$this->vars[$name]:null;}
    public function __set($name,$value){
        $this->vars[$name]=$value;
        if($this->cache!==false)$this->cache['vars'][$name]=$value;
    }
    public function startCache($key){
        if(($data=Single::instance()->cache->get($key))!==false){
            $this->vars+=$data['vars'];
            echo $data['contents'];
            return false;
        }
        $this->contents.=ob_get_clean();
        $this->cache=array('key'=>$key,'vars'=>array());
        ob_start();
        return true;
    }
    public function endCache($time=60){
        $this->cache['contents']=ob_get_clean();
        $this->contents.=$this->cache['contents'];
        Single::instance()->cache->set($this->cache['key'], $this->cache, $time);
        $this->cache=false;
        ob_start();
    }
    public function flush(){
        $this->contents.=ob_get_clean();
        if(!empty($this->layout)&&$this->contents!==''){
            require BASE.(empty(Single::instance()->config['views_path'])?'views':Single::instance()->config['views_path']).DIRECTORY_SEPARATOR.$this->layout;
        }else echo $this->contents;
        $this->contents='';
    }
    public function view($file, array $vars=array()){
        extract($vars);
        include BASE.(empty(Single::instance()->config['views_path'])?'views':Single::instance()->config['views_path']).DIRECTORY_SEPARATOR.$file;
    }
    public function error($errno=404,$msg='An error was encountered',$fatal=false){
        if(!empty(Single::instance()->config['error_'.$errno])) include BASE.(empty(Single::instance()->config['views_path'])?'views':Single::instance()->config['views_path']).DIRECTORY_SEPARATOR.Single::instance()->config['error_'.$errno];
        else echo '<h1>Error '.$errno.'</h1><br/>'.$msg;
        if($fatal)exit;
    }
}
class Single_Cache{
    public function get($key, $deleteAfter=false){
        $file=$this->getFile($key);
        if(!file_exists($file))return false;
        $data=unserialize(file_get_contents($file));
        if($data['deletion_time']<time()){
            unlink($file);
            return false;
        }
        if($deleteAfter)unlink($file);
        return $data['content'];
    }
    public function set($key,$value,$time=60){
        $file=$this->getFile($key);
        if(!is_dir(dirname($file)))mkdir(dirname($file),0777,true);
        $data=array('content'=>$value,'deletion_time'=>time()+$time);
        return file_put_contents($file, serialize($data));
    }
    public function delete($key){return unlink($this->getFile($key));}
    private function getFile($key){return BASE.'cache'.DIRECTORY_SEPARATOR.str_replace('.', DIRECTORY_SEPARATOR, $key).'.cache';}
}
/**
 * @property-read Single_Output $output
 * @property-read Single_Cache $cache
 * @property-read Single_Loader $loader
 */
abstract class Single_Controller extends Single_Component{}
class Single_Database extends PDO{
    public function __construct(){
        $config=empty(Single::instance()->config['database'])?array():Single::instance()->config['database'];
        $opt=(empty($config['options'])?array():(array)$config['options']) + array(PDO::ATTR_ERRMODE=>PDO::ERRMODE_EXCEPTION,PDO::ATTR_STATEMENT_CLASS=>array('Single_Statement'));
        try{parent::__construct(empty($config['dsn'])?'mysql:host=127.0.0.1;dbname=single':$config['dsn'], empty($config['user'])?'root':$config['user'], empty($config['pass'])?'':$config['pass'],$opt);}
        catch(Exception $e){echo '<pre>'.$e.'</pre>';exit;}
    }
    /** @return Single_Database */
    public static function instance(){
        if(Single::instance()->database===null)Single::instance()->database=new self;
        return Single::instance()->database;
    }
}
class Single_Statement extends PDOStatement{
    public function fetch($fetch_style=null,$cursor_orientation=PDO::FETCH_ORI_NEXT,$cursor_offset=0){
        if($fetch_style===null)$this->setFetchMode(PDO::FETCH_CLASS, 'Single_ActiveRecord',array(false));
        return parent::fetch($fetch_style, $cursor_orientation, $cursor_offset);
    }
    public function fetchAll($fetch_style=PDO::FETCH_CLASS,$fetch_argument='Single_ActiveRecord',$ctor_args=array(false)){return parent::fetchAll($fetch_style, $fetch_argument, $ctor_args);}
}
class Single_ActiveRecord implements Iterator{
    private $vars=array(),$old_vars=array(),$is_new;
    public function __construct($is_new=true){$this->is_new=$is_new;}
    public function __get($name){return isset($this->vars[$name])?$this->vars[$name]:null;}
    public function __set($name,$value){
        if(!isset($this->vars[$name]))$this->old_vars[$name]=$value;
        $this->vars[$name]=$value;
    }
    public function __sleep(){return array_keys($this->vars);}
    public function current(){return current($this->vars);}
    public function next(){return next($this->vars);}
    public function rewind(){return reset($this->vars);}
    public function key(){return key($this->vars);}
    public function valid(){return isset($this->vars[$this->key()]);}
    public function save($table){
        if($this->is_new)return Single_Database::instance()->prepare('INSERT INTO '.addslashes($table).'('.implode(', ', array_keys($this->vars)).') VALUES(:'.implode(',:',array_keys($this->vars)).')')->execute($this->vars);
        $tmp1=$tmp2=$tmp3=array();
        foreach($this->vars as $col=>$value){$tmp1[]=$col.'= :'.$col;}
        foreach($this->old_vars as $col=>$value){
            $tmp2[]=$col.'=:old_'.$col;
            $tmp3['old_'.$col]=$value;
        }
        $this->old_vars=$this->vars;
        return Single_Database::instance()->prepare('UPDATE '.addslashes($table).' SET '.implode(', ', $tmp1).' WHERE '.implode(' AND ',$tmp2))->execute($this->vars+$tmp3);
    }
    public function delete($table){
        $tmp=array();
        foreach($this->vars as $col=>$value)$tmp[]=$col.'= :'.$col;
        return Single_Database::instance()->prepare('DELETE FROM '.addslashes($table).' WHERE '.implode(' AND ', $tmp))->execute($this->vars);
    }
}
/** @property-read Single_Database $database */
abstract class Single_Model extends Single_Component{
    public function __construct() {
        parent::__construct();
        $this->database=Single_Database::instance();
    }
}
?>

 

 

Cordialement,

Hey hey

Link to comment
Share on other sites

  • Replies 2
  • Created
  • Last Reply

Top Posters In This Topic

Top Posters In This Topic

Hey!

Super boulot :). J'adore l'idée de petit et puissant (tiens la publicité pour la léssive AJAX (Skip petit et puissant ^^)).

Merci du travail effectué :).

 

Bonne chance pour Diamond.

 

Salutations,

ThanosS.

Link to comment
Share on other sites

Hey!

Super boulot :). J'adore l'idée de petit et puissant (tiens la publicité pour la léssive AJAX (Skip petit et puissant ^^)).

Merci du travail effectué :).

 

Bonne chance pour Diamond.

 

Salutations,

ThanosS.

 

lol :D Merci, ça chance de aidemu :surprised:

Link to comment
Share on other sites

Please sign in to comment

You will be able to leave a comment after signing in



Sign In Now
 Share



  • Flux d'Activité

    1. 30

      AresiaCMS

    2. 30

      AresiaCMS

    3. 30

      AresiaCMS

    4. 66

      Decay 2.51

    5. 0

      PACK Dofus Arcenciel

    6. 0

      Partage Ornament L'attaque des Titans

  • Recently Browsing

    • No registered users viewing this page.

Important Information

Terms of Use / Privacy Policy / Guidelines / We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.