Commit 9f626213 authored by Andre's avatar Andre

v2.0.0

parent ff90f07d
Pipeline #17672461 passed with stage
in 2 seconds
......@@ -6,7 +6,7 @@
**Code golf** is a type of recreational computer programming competition in which participants strive to achieve
the shortest possible source code that implements a certain algorithm. <sup>[\[source\]](https://en.wikipedia.org/wiki/Code_golf)</sup>
**Picco** is a tiny PHP web framework that only takes **~2,5 kB** of space and has no dependencies on other libraries,
**Picco** is a tiny PHP web framework that only takes **~2,3 kB** of space and has no dependencies on other libraries,
while still providing quite a lot of features, being extensible and reasonably easy to use.
### Features
......@@ -24,6 +24,7 @@ while still providing quite a lot of features, being extensible and reasonably e
- It **shouldn't look golfed** from the outside, i.e. "nice" namespaces, classnames and public method names are required
- Compromise between readability and brevity: new lines should be used to separate significant blocks of code, but indentation is unwanted
- Picco is a Composer package with **no dependencies**
- No warnings allowed either
### Installation
......@@ -40,10 +41,12 @@ To create database schema and fill it with some dummy data, run:
Also, configure the directory for the runtime files:
chmod -R 777 run
You can run it using the PHP built-in server:
php -S localhost:8000 -t web/
You should be able to access the project
at `http://localhost/my_new_project/web/app_dev.php` (for development environment)
and `http://localhost/my_new_project/web` (for production environment).
You should be able to access the project at `http://localhost:8000`.
### Usage
......@@ -102,10 +105,10 @@ They have access to the router (`$r`) and the values returned by the controller
There are 5 constants defined by Picco:
* **D** -- root directory of the project, useful for instance for loading configuration files, setting up filesystem cache, etc.
* **F** -- base of the URL (like `picco/web/app_dev.php`), useful for generating links to other pages, if you don't want to use the router.
* **F** -- base of the URL (like `picco/web/index.php`), useful for generating links to other pages, if you don't want to use the router.
* **B** -- base of the URL, but without front controller (like `picco/web/`), useful for including assets, eg. `<img src=<?=B?>logo.png`.
* **R** -- current route
* **E** -- current environment: `true` for dev/debug mode (`app_dev.php`), `false` for prod mode (`app.php`)
* **E** -- current environment: `true` for dev/debug mode (default), `false` for prod mode (if `APP_DEBUG` env var is set to `0`).
#### Dependency Injection Container
......@@ -159,7 +162,7 @@ You can overwrite that default behaviour by listening to the `error` event.
Similarly to controllers, there is an `App\Tasks` class, public methods of which are CLI tasks.
If you run, for instance, `bin/picco test foo bar`, it will execute `App\Tasks::test('foo', 'bar')`.
If you run, for instance, `vendor/bin/picco test foo bar`, it will execute `App\Tasks::test('foo', 'bar')`.
### Contributing
......
......@@ -3,6 +3,7 @@
"description": "Golfy PHP framework",
"type": "library",
"license": "MIT",
"homepage": "https://picco.avris.it",
"authors": [
{
"name": "avris",
......@@ -10,7 +11,9 @@
"homepage": "https://avris.it"
}
],
"require": {},
"require": {
"php": "^7.0"
},
"autoload": {
"psr-4": { "Picco\\": ["src"] }
},
......
#!/usr/bin/env php
<?while(@$i<4)if(file_exists($l=($d=__DIR__.str_repeat('/..',@$i++)).'/vendor/autoload.php')){require$l;break;}
define('D',"$d/");define('AB',"$d/web");define('E',true);
<?require($d=__DIR__ . '/../../').'autoload.php';
define('D',"$d/../");define('AB',"$d/../web");define('E',1);
$c=new Picco\Container;$c->dispatcher=new Picco\Dispatcher;App\Services::get($c);$a=$argv;$s='array_shift';$s($a);
if(!is_callable($d=[new App\Tasks,$m=$s($a)?:'help']))throw new Exception("Undefined task $m");call_user_func_array($d,array_merge([$c],$a));
\ No newline at end of file
<?namespace Picco;class App{
function __construct($d,$e){session_start();$s=$_SERVER;define('D',$d);$l=strlen($r=@$s['PATH_INFO']);$u=explode('?',$s['REQUEST_URI'])[0];define('F',$l?substr($u,0,-$l):$u);define('R',rtrim($r,'/')?:'/');define('B',preg_replace('#/[^/]+\.php$#','',F).'/');define('E',$e);}
function __construct($d,$e){session_start();define('D',$d);$u=explode('?',($s=$_SERVER)['REQUEST_URI'])[0];$l=strlen($r=@$s['PATH_INFO'])?:$u=='/';define('F',$l?substr($u,0,-$l):$u);define('R',rtrim($r,'/')?:'/');define('B',preg_replace('#/[^/]+\.php$#','',F).'/');define('E',$e);}
function run(){$c=new Container();$c->router=new Router(\App\Routing::get());$c->view=new View;$q=$c->controllers=new\App\Controllers;$d=$c->dispatcher=new Dispatcher;\App\Services::get($c);
$t=function($f,$m)use($c,$q,$d){$r=(object)call_user_func_array([$q,$f],$m);return$d->response($c,$r)?:$c->view->render($f,(array)$r+['r'=>$c->router]);};
$d->error=function($c,$e)use($t){if(E)throw$e;return$t('error',[$c,$e]);};
......
<?namespace Picco;class Container{protected$s,$r;
<?namespace Picco;class Container{private$s,$r;
function __set($n,$v){$this->s[$n]=$v;}
function __get($n){return isset($this->r[$n])?$this->r[$n]:$this->r[$n]=is_callable($s=$this->s[$n])&&!$s instanceof Dispatcher?$s($this):$s;}}
\ No newline at end of file
function __get($n){return($t=$this)->r[$n]??$t->r[$n]=($s=$t->s[$n])instanceof\Closure?$s($t):$s;}}
\ No newline at end of file
<?namespace Picco;class Dispatcher{protected$e;
<?namespace Picco;class Dispatcher{private$e;
function __set($n,$c){$this->e[$n][]=$c;}
function __call($n,$a){foreach(@$this->e[$n]?:[]as$e)if(null!==$r=call_user_func_array($e,$a))return$r;}}
\ No newline at end of file
function __call($n,$a){foreach($this->e[$n]??[]as$e)if(null!==$r=call_user_func_array($e,$a))return$r;}}
\ No newline at end of file
<?namespace Picco;class Router{protected$r;
<?namespace Picco;class Router{private$r;
function __construct($r){$this->r=$r;}
function match($u){foreach($this->r as$n=>$p)if(preg_match("#^$p$#",$u,$m))return[$n,$m];throw new\Exception("Route for $u not found",404);}
function match($u){foreach($this->r as$n=>$p)if(preg_match("#^$p$#",$u,$m))return[$n,$m];throw new\Exception("$u not found",404);}
function get($c,$o=[]){return F.(rtrim(strtr(preg_replace_callback('#\([^(]*\)#',function()use($o){static$count=0;return@$o[$count++];},$this->r[$c]),['?'=>'']),'/')?:'/');}}
\ No newline at end of file
<?namespace Picco;class View{function render($v,$p=[]){unset($p['v']);extract($p);ob_start();require D."views/$v.phtml";return ob_get_clean();}}
\ No newline at end of file
<?namespace Picco;class View{function render($v,$p){unset($p['v']);extract($p);ob_start();require D."views/$v.phtml";return ob_get_clean();}}
\ No newline at end of file
......@@ -40,7 +40,7 @@ a($a->run() === $expected . "\n");
Mock::$routing = [];
Mock::$services = function(Container $c) { };
a($a->run() === "<p>message = Route for /test not found</p>\n");
a($a->run() === "<p>message = /test not found</p>\n");
Mock::$routing = [ 'foo' => '/test' ];
Mock::$services = function(Container $c) { $c->dispatcher->request = function() { return 'blocked'; }; };
......@@ -60,4 +60,4 @@ a($a->run() === $expected . "\n");
Mock::$routing = [];
Mock::$services = function(Container $c) { $c->dispatcher->error = function(Container $c, \Exception $e) { return $e->getMessage(); }; };
a($a->run() === "Route for /test not found");
a($a->run() === "/test not found");
......@@ -25,7 +25,7 @@ function assertRouteNotFound($url) {
$r->match($url);
a(false);
} catch (Exception $e) {
a($e->getMessage() === "Route for $url not found");
a($e->getMessage() === "$url not found");
a($e->getCode() === 404);
}
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment