Coder Social home page Coder Social logo

limonade's Introduction

Limonade: README

Limonade is a PHP micro framework for rapid web development and prototyping.

It's inspired by frameworks like Sinatra or Camping in Ruby, or Orbit in Lua. It aims to be simple, lightweight and extremly flexible.

Limonade provides functions that complete the PHP basic set, while keeping consistency with native functions and sitting up on them.

Limonade is easy to learn and provides everything that you can expect from a modern framework (MVC, REST, ...)

    require_once 'lib/limonade.php';
    dispatch('/', 'hello');
        function hello()
        {
            return 'Hello world!';
        }
    run();

About this document

This document provides a quick, but comprehensive, guide of Limonade features.

For more informations, you can see the website, examples, and of course the source code which is still the best documentation.

A discussion group is also available for more exchanges.

Requirements

  • PHP 5.1.6 > (successfully tested with PHP 5.1.6 but it might work with older versions)

Routes

Routes combine

  • an HTTP method
  • with an URL matching pattern
  • and a callback parameter

So they make the glue between an URL + a HTTP method, and the code provided in a callback controller.

    dispatch('/', 'my_get_function');
    # same as dispatch_get('my_get_function');
        function my_get_function()
        {
            // Show something
            // with the code of this callback controller
        }
    
    dispatch_post('/', 'my_post_function'); 
        function my_post_function()
        {
            // Create something
        }
        
    dispatch_put('/', 'my_update_function'); 
        function my_update_function()
        {
            // Update something
        }
        
    dispatch_delete('/', 'my_delete_function'); 
        function my_delete_function()
        {
            // Delete something
        }

    dispatch_patch('/', 'my_patch_function');
        function my_patch_function()
        {
            // Patch something
        }

Routes are matched in the order they are declared. The search is performed with a path given through browser URL:

http://localhost/my_app/?u=/my/path
http://localhost/my_app/?uri=/my/path
http://localhost/my_app/index.php?/my/path
http://localhost/my_app/?/my/path

When PUT,DELETE or PATCH methods are not supported (like in HTML form submision), you can use the _method parameter in POST requests: it will override the POST method.

    <form action="<?php echo url_for('profile_update'); ?>" method="post">
        <p><input type="hidden" name="_method" value="PUT" id="_method"></p>
        <p>... your form fields</p>
        <p><input type="submit" value="Update"></p>
    </form>

Routing patterns and parameters

Patterns may include named parameters. Associated values of those parameters are available with the params() function.

    dispatch('/hello/:name', 'hello');
        function hello()
        {
            $name = params('name');
            return 'Hello $name';
        }

Patterns may also include wildcard parameters. Associated values are available through numeric indexes, in the same order as in the pattern.

    dispatch('/writing/*/to/*', 'my_letter');
        function my_letter()
        {
            # Matches /writing/an_email/to/joe
            $type = params(0); # "an_email"
            $name = params(1); # "joe"
            # ...
        }
        
    dispatch('/files/*.*', 'share_files');
        function share_files()
        {
            # matches /files/readme.txt
            $ext = params(1);
            $filename = params(0).".".$ext;
            # ...
        }

Unlike the simple wildcard character *, the double wildcard character ** specifies a string that may contain a /

    dispatch('/files/**', 'share_files')
        function share_files()
        {
            # Matches /files/my/own/file.txt
            $filename = params(0); # my/own/file.txt
        }

Pattern may also be a regular expression if it begins with a ^

    dispatch('^/my/own/(\d+)/regexp', 'my_func');
        function my_func()
        {
            # matches /my/own/12/regexp
            $num = params(0);
        }

Wildcard parameters and regular expressions may be named, too.

    dispatch(array('/say/*/to/**', array("what", "name")), 'my_func');
        function my_func()
        {
            # Matches /say/hello/to/joe
            $what = params('what');
            $name = params('name');
        }

You can also provide default parameter values that are merged with and overriden by the pattern parameters.

    $options = array('params' => array('firstname'=>'bob'));
    dispatch('/hello/:name', 'hello', $options);
        function hello($firstname, $name) # default parameters first
        {
            return 'Hello $firstname $name';
        }

Callback controllers

The callback can be a function, an object method, a static method or a closure. See php documentation to learn more about the callback pseudo-type.

    # will call my_hello_function() function
    dispatch('/hello', 'my_hello_function');

    # Static class method call, MyClass::hello();
    dispatch('/hello', array('MyClass', 'hello'));

    # Object method call, $obj->hello();
    dispatch('/hello', array($obj, 'hello'));

    # Static class method call (As of PHP 5.2.3), MyClass::hello();
    dispatch('/hello', 'MyClass::hello');

    # Using lambda function (As of PHP 5.3.0)
    dispatch('/hello', function(){
      return 'Hello World!';
    });

Callback controllers return the rendered view output (see Views and templates).

They can take the pattern parameters as arguments

    dispatch('/hello/:firstname/:name', 'hello');
        function hello($firstname, $name)
        {
            # $firstname parameter equals params('firstname');
            # and $name parameter equals params('name');
            return 'Hello $firstname $name';
        }

Callbacks called by routes can be written anywhere before the execution of the run() function. They can also be grouped in controller files stored in a controllers/ folder.

/                   # site root
 - index.php        # file with routes declarations and run()
 + controllers/
     - blog.php     # functions for blog: blog_index(), blog_show(),
                    #  blog_post()...
     - comments.php # comments_for_a_post(), comment_add()...

This folder location can be set with the controllers_dir option.

    option('controllers_dir', dirname(__FILE__).'/other/dir/for/controllers');

You can also define autoload_controller function to load controllers in your own way:

    function autoload_controller($callback) 
    { 
       # If $callback, the callback function defined in matching route, 
       # begins with 'admin_', then we load controllers from
       # the admin sub-directory in the controllers directory.
       # Else we load controllers the normal way from 'controllers_dir'.
       
       $path = option('controllers_dir'); 
       if(strpos($callback, "admin_") === 0) $path = file_path($path, 'admin'); 
       require_once_dir($path); 
    }

Url rewriting

Since version 0.4.1, Limonade supports url rewriting.

If you use Apache, with a .htaccess in your app folder

<IfModule mod_rewrite.c>
  Options +FollowSymlinks
  Options +Indexes
  RewriteEngine on
  
  # if your app is in a subfolder
  # RewriteBase /my_app/ 

  # test string is a valid files
  RewriteCond %{SCRIPT_FILENAME} !-f
  # test string is a valid directory
  RewriteCond %{SCRIPT_FILENAME} !-d

  RewriteRule ^(.*)$   index.php?uri=/$1    [NC,L,QSA]
  # with QSA flag (query string append),
  # forces the rewrite engine to append a query string part of the
  # substitution string to the existing string, instead of replacing it.
</IfModule>

If you use Nginx, add the following to your server declaration

server {
    location / {
        
        try_files $uri $uri/ @rewrite;
    }
    location @rewrite {
        rewrite ^/(.*)$ /index.php?u=$1&$args;
    }
}

then remember to set explicitly the option('base_uri') in your configure() function:

    option('base_uri', '/my_app'); # '/' or same as the RewriteBase in your .htaccess

You can access your site with urls like http://your.new-website.com/my/limonade/path instead of http://your.new-website.com/?/my/limonade/path.

Views and templates

Template files are located by default in views/ folder. Views folder location can be set with the views_dir option.

    option('views_dir', dirname(__FILE__).'/other/dir/for/views');

To pass variables to templates, we use the function set ()

    set('name', 'John Doe');
    render('index.html.php');

Variables may also be passed directly:

    render('index.html.php', null, array('name' => 'John Doe' ));

set_or_default function allows passing a variable, and if it's empty, a default value. It is really useful for the assignment of optional parameters extracted from the url using the params() function.

    dispatch('/hello/:name', 'hello');
        function  hello()
        {
            # matching /hello/
            set_or_default('name', params('name'),'John');
            return render('Hello %s!'); // returns 'Hello John!' because params('name') was empty. Else it would have returned params('name') value.
        }

As you can notice, final output is returned by your controller. So remember to explicitly return your view in your controller with the return keyword! (This remark will be particularly helpful for rubyists ;-) )

Layouts

Templates may be rendered inside another template: a layout.

Layout may be set with the layout function:

    layout('default_layout.php');

or directly with the template rendering function

    render('index.html.php', 'default_layout.php');

If layout value is null, rendering will be done without any layout.

    render('index.html.php', null);

Formatted strings and inline templates

Formatted string can be used like with sprintf:

    set('num', 5);
    set('where', 'tree');
    return render('There are %d monkeys in the %s') // returns 'There are 5 monkeys in the tree'

It's also possible to provide a function name as a template. By this way, for example, we can produce a single file application.

    function html_message($vars){ extract($vars);?>
        <h1>Title: <?php echo h($title); ?></h1>
        <p>Message:<br>
           <?php echo h($msg); ?></p>
    <?}
    
    // in a request handling function
    set('title', 'Hello!');
    set('msg', 'There are 100 monkeys in the Chennai and bangalore');
    return render('html_message');

HTML Templates

html function is used in the same way as render. A header specifies the proper HTTP Content-type (text/html) and encoding setting defined through options (utf8 by default).

    html('my_template.html.php');

Templates XML

xml function is used in the same way as render. A header specifies the proper HTTP Content-type (text/xml) and encoding setting defined through options (utf8 by default).

    xml('my_template.xml.php');

Templates CSS

css function is used in the same way as render. A header specifies the proper HTTP Content-type (text/css) and encoding setting defined through options (utf8 by default).

    css('screen.css.php');

Templates JS

js function is used in the same way as render. A header specifies the proper HTTP Content-type (application/javascript) and encoding setting defined through options (utf8 by default).

    js('app.js.php');

Templates TXT

txt function is used in the same way as render. A header specifies the proper HTTP Content-type (text/plain) and encoding setting defined through options (utf8 by default).

    txt('index.txt.php');

Templates JSON

json is used the same way as json_encode function, and returns a string containing the JSON representation of a value. A header specifies the proper HTTP Content-type (application/x-javascript) and encoding setting defined through options (utf8 by default).

    json($my_data);

Serving files

The render_file function can render a file directly to the ouptut buffer.

    render_file(option('public_dir').'foo.jpg');

A header specifies the proper HTTP Content-type depending on the file extension, and for text files, encoding setting defined through options (utf8 by default) .

Output is temporized so that it can easily handle large files.

Partials

The partial function is a shortcut to render with no layout. Useful for managing reusable blocks and keeping them in separate files.

This code

    partial('my_posts.php', array('posts'=>$posts));

is the same as

    render('my_posts.php', null, array('posts'=>$posts));

Captures

The content_for function allows you to capture a block of text in a view. Then the captured block will be available for the layout. This is useful for management of layout regions like a sidebar or to set javascript or stylesheet files that are specific to a view.

For example with this layout:

    <div id="content">
      <div id="main">
        <?php echo $content; ?>
      </div>
      <div id="side">
        <?php if (isset($side)) echo $side; ?>
      </div>
    </div>

And in your view:

    <p>My main content</p>
    
    <?php content_for('side'); ?>
    <ul>
      <li><a href="<?php echo url_for('/pages/item1')?>">Item 1</a></li>
      <li><a href="<?php echo url_for('/pages/item2')?>">Item 2</a></li>
    </ul>
    <?php end_content_for(); ?>

Rendered result is:

    <div id="content">
      <div id="main">
        <p>My main content</p>
      </div>
      <div id="side">
        <ul>
          <li><a href="?/pages/item1">Item 1</a></li>
          <li><a href="?/pages/item1">Item 2</a></li>
        </ul>
      </div>
    </div>

The above example is detailed in this tutorial.

Use captures with partials, it will help you to organize your views and will keep you from having to copy/paste the same code many times.

Hooks and filters

Limonade allows the user to define some functions to enhance the Limonade behaviour with its own needs.

Some of those, like the before hook and the after filter are commonly used, and others are only for advanced usage that might require a good comprehension of Limonade internals.

Before

You can define a before function that will be executed before each request. This is very useful to define a default layout or for passing common variables to the templates.

    function before($route)
    {
        layout('default_layout.php');
        set('site_title', 'My Website');
    }

The current matching route is also passed to the before function, so you can test it. It's an array as returned by the internal route_find function, with these values:

  • method (HTTP method)
  • pattern (regexp pattern)
  • names (params names)
  • callback (callback)
  • options (route options)
  • params (current params)

After

An after output filter is also available. It's executed after each request and can apply a transformation to the output (except for render_file outputs which are sent directly to the output buffer).

    function after($output){
      $config = array('indent' => TRUE,
                      'output-xhtml' => TRUE,
                      'wrap' => 200);
      
      $encoding = strtoupper(str_replace('-','', option('encoding')));
      $tidy = tidy_parse_string($output, $config, $encoding);
      $tidy->cleanRepair();
      return $tidy;
    }

The current executed route is also available for after function.

Before render

You can define a before_render function that will filter your view before rendering it.

The first three parameters are the same as those passed to the render function:

  • $content_or_func: the view string
  • $layout: current layout path
  • $locals: variables passed directly to the render function

Last parameter, $view_path is by default file_path(option('views_dir'), $content_or_func);

    function before_render($content_or_func, $layout, $locals, $view_path)
    {
      # Transform $content_or_func, $layout, $locals or $view_path.
      # Then return there new values
      return array($content_or_func, $layout, $locals, $view_path);
    }

Autorender

You can define your own autorender function to make automatic rendering depending on current matching route. It will be executed if your controller returns a null output.

    dispatch('/', 'hello');
    function hello()
    {
        # process some stuff...
        set('name', 'Bob');
        
        # but don't return anything
        # ( like if you were ending this function with return null; )
    }
    
    function autorender($route)
    {
        $view = $route['callback'] . ".html.php";
        return html($view);
    }

In this example, when url / is called, hello() is executed and then autorender() renders the matching hello.html.php view.

Before exit

If you define a before_exit, it is called at the begining of the stop/exit process (stop_and_exit function called automatically at Limonade application termination).

    function before_exit($exit)
    {
        # $exit is the same parameter as the one passed to `stop_and_exit`.
        # If it's false, the exit process will not be executed, 
        # only the stop instructions
        # by default it is true
    }

Before sending a header

You can define a before_sending_header function that will be called before Limonade emits a header() call. This way you can add additional headers:

    dispatch('/style.css', 'css');
    function css()
    {
        # Generate css file and output
        return css('style.css.php');
    }

    function before_sending_header($header)
    {
        if (strpos($header, 'text/css') !== false)
        {
            # intercept text/css content-type and add caching to the headers
            send_header("Cache-Control: max-age=600, public");
        }
    }

Caution: Take care not to cause a loop by repeatedly calling send_header() from the before_sending_header() function!

Configuration

You can define a configure that will be executed when application is launched (at the begining of the run execution). You can define options inside it, a connection to a database ...

    function configure()
    {
        $env = $_SERVER['HTTP_HOST'] == "localhost" ? ENV_DEVELOPMENT : ENV_PRODUCTION;
        option('env', $env);
        if(option('env') > ENV_PRODUCTION)
    	{
    		options('dsn', 'sqlite:db/development.db'));
    	}
    	else
    	{
    	    options('dsn', 'sqlite:db/production.db'));
    	}
        $GLOBALS['my_db_connexion'] = new PDO(option('dsn'));
    }

PHP files contained in the option('lib_dir') folder (lib/ by default) are loaded with require_once just after executing configure. So you can place in this folder all your PHP libraries and functions so that they will be loaded and available at application launch.

Options

The option function allows you to define and access the options of the application.

    option('env', ENV_PRODUCTION);
    option('env'); // return ENV_PRODUCTION value

If the name of option is not specified, it returns an array of all the options set.

You can use it to manage Limonade options and your own custom options in your application.

Default Limonade options have the following values:

    option('root_dir',           $root_dir); // this folder contains your main application file
    option('base_path',          $base_path);
    option('base_uri',           $base_uri); // set it manually if you use url_rewriting
    option('limonade_dir',       dirname(__FILE__).'/'); // this fiolder contains the limonade.php main file
    option('limonade_views_dir', dirname(__FILE__).'/limonade/views/');
    option('limonade_public_dir',dirname(__FILE__).'/limonade/public/');
    option('public_dir',         $root_dir.'/public/');
    option('views_dir',          $root_dir.'/views/');
    option('controllers_dir',    $root_dir.'/controllers/');
    option('lib_dir',            $root_dir.'/lib/');
    option('error_views_dir',    option('limonade_views_dir'));
    option('env',                ENV_PRODUCTION);
    option('debug',              true);
    option('session',            LIM_SESSION_NAME); // true, false or the name of your session
    option('encoding',           'utf-8');
    option('x-sendfile',         0); // 0: disabled, 
                                     // X-SENDFILE: for Apache and Lighttpd v. >= 1.5,
                                     // X-LIGHTTPD-SEND-FILE: for Apache and Lighttpd v. < 1.5

Sessions

Session starts automatically by default. Then you can access session variables like you used to do, with $_SESSION array.

You can disable sessions with the session option.

see snippet example

Flash

Flash is a special use of sessions. A flash value will be available only on next request and will be deleted after. It's very useful to raise errors on a form or to notice a successful action.

  • flash($name, $value...) defines a flash for the next request
  • in views, you can get current flash values with the $flash array or flash_now($name) function.

see snippet example

Helpers

See sources or api for more about all available helpers.

url_for

You can use the url_for function for rendering limonade urls. They will be well formed from whatever folder in the document root your application is installed on your web server.

    # with option('base_uri', '?')
    url_for('one', 'two', 'three'); # returns ?/one/two/three
    url_for('one', 'two', array('page' => 1)); # returns ?/one/two&amp;page=2

If you want to use url rewriting, you need to explicitly set the base_uri option ( default is /your_file_path/?)

Halting and error handling

Halt

You can stop immediately the execution of the application with the halt function. Errors will be handled by default Limonade error handlers or those you have defined.

    halt(NOT_FOUND);
    halt("En error occured in my app...");

Not Found

By default, displays the not_found error output function and sends a 404 NOT FOUND HTTP header.

    halt(NOT_FOUND);
    halt(NOT_FOUND, "This product doesn't exists.");

To define a new view for this error, you can simply declare a not_found function.

    function not_found($errno, $errstr, $errfile=null, $errline=null)
    {
        set('errno', $errno);
        set('errstr', $errstr);
        set('errfile', $errfile);
        set('errline', $errline);
        return html("show_not_found_errors.html.php");
    }

Server Error

By default, displays the server_error error output function and sends a 500 INTERNAL SERVER ERROR HTTP header.

    halt();
    halt('Breaking bad!');
    halt(SERVER_ERROR, "Not good...");
    trigger_error("Wrong parameter", E_USER_ERROR);

PHP errors are also caught and sent to this error handler output.

To define a new view for this error, you can simply declare a server_error function.

    function server_error($errno, $errstr, $errfile=null, $errline=null)
    {
        $args = compact('errno', 'errstr', 'errfile', 'errline');	
        return html("show_server_errors.html.php", error_layout(), $args);
    }

Error layout

Allows you to define and access a layout dedicated to errors.

    error_layout('error_layout.php');
    error_layout(); // return 'error_layout.php'

Error handling

In addition to the common NOT_FOUND and SERVER_ERROR error displays, Limonade can redirect precise errors to your own functions.

    error(E_USER_WARNING, 'my_notices')
        function my_notices($errno, $errstr, $errfile, $errline)
        {
            // storing php warnings in a log file
            // ...
            status(SERVER_ERROR);
            return html('<h1>Server Error</h1>');
        }

E_LIM_HTTP means all HTTP errors

    error(E_LIM_HTTP, 'my_http_errors')
        function my_http_errors($errno, $errstr, $errfile, $errline)
        {
            status($errno);
            return html('<h1>'.http_response_status_code($errno).'</h1>');
        }

E_LIM_PHP means all PHP errors (sent by PHP or raised by the user through trigger_error function).

Other useful functions

Limonade also provides a useful set of functions that can help you managing files, HTTP… For more about those utilities, see the source code at section 7. UTILS.

Testing

[TODO]

More

limonade's People

Contributors

16 avatar abrcoelho avatar apankov avatar artiom avatar bfontaine avatar cbeerta avatar cbrumelle avatar datashaman avatar dunhamjared avatar eitland avatar fyears avatar gi-lunaweb avatar hellogerard avatar jackhammermad avatar jblanche avatar jsor avatar kematzy avatar kostadinnp avatar lanji avatar neochrome avatar sloonz avatar sniemela avatar sofadesign avatar styxua avatar sudwebdesign avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

limonade's Issues

Multiple directory for includes

Could be useful to be able to add more include folder for controllers, views, lib, etc.
Use case: Multiple apps (with multiple frontend controller (index.php...)) could load only the code it need + generic code (like, an app with the same layout as an other).
With this, it could be more simpler to share code between apps, too.

by exemple:

option('views_dir', dirname(FILE).'/views');

can become:

option('views_dir', array(dirname(FILE).'/views', dirname(FILE).'/myapp/views'));

here, /views is my genereic views folder, and /myapp/views is my app folder.
The latest is the first to be search. Like that, it's possible to override generic template with an app template.

Thanks.

Chrome's HEAD request wipes flash messages

I'm running apache, and php in fcgid on a locahost (not sure if this is relavent, but there you go)

Chrome is sending a HEAD request immediately after each GET or POST

As flash messages are destroyed on the next request the HEAD request is wiping them before the next GET or POST

the flow goes a little like this

GET /step1:
flash("message","hello");

HEAD /step1
flash_sweep(); // destroys flash message

GET /step2
flash_now("message") // returns null

I can't do a pull request as I've quite substantially forked other parts, but the fix I've implement is in the stop_and_exit definition changing:

if($flash_sweep) flash_sweep();

to:

if($flash_sweep && !request_is_head()) flash_sweep();

Make users call url_for

I saw the improvement to redirect_to. Totally agree.
From my own experience, I'd say that whenever you expect a url, make the use decide if they need to call url_for. Else you'll end up with lots of different functions that accept a url parameter, and for consistency Limonade will have to accept all the parameters that url_for accepts in that place, and call it magically.

This will not improve code readability. Compare
#1 redirect_to('user', 'settings', array('status'=>HTTP_SEE_OTHER));
#2 redirect_to(url_for('user', 'settings'), array('status'=>HTTP_SEE_OTHER));

Someone that is new to Limonade will immediately understand #2, and need to think about #1.

Undefined variables in run() method

limonade/lib/limonade.php

Lines 390 to 396 in be59a72

if(!function_exists('route_missing'))
{
function route_missing($request_method, $request_uri)
{
halt(NOT_FOUND, "($request_method) $request_uri");
}
}

$request_methodand $request_uri must be defined before !

For now, they are only defined a few lines after (in section 6. Check request)

limonade/lib/limonade.php

Lines 400 to 401 in be59a72

# 6. Check request
if($rm = request_method($env))

limonade/lib/limonade.php

Lines 408 to 409 in be59a72

# 6.1 Check matching route
if($route = route_find($rm, request_uri($env)))

Order of configure() / loading from lib/

The documentation says libraries are loaded before configure():

PHP files contained in the option('lib_dir') folder (lib/ by default) are loaded with require_once just before executing configure. So you can place in this folder all your PHP libraries and functions so that they will be loaded and available at application launch.

However the source code runs configure() before loading libraries:

#2. Set user configuration
call_if_exists('configure');

[...]

#3. Loading libs
require_once_dir(option('lib_dir'));

This broke an app I was writing when I assumed that my ORM in lib/ would be loaded before configure() was executed. Which is the intended behavior, please?

Project still maintained?

Hello Fabrice,
I wanted to ask if this awesome project is still maintained and being worked on. The last commit was in 2015. 2 years ago....
There are still lots of todos that are not done yet. The current version is 0.5.1 and there are todos until version 1.0!!!
If there is nobody who is currently working on new features, maybe I can overtake the project?
Short info about me: I'm 15 years old, from Austria, and have about two and a half years of experience in PHP. I have never maintained such a large project and am a bit unexperienced in it.

If you have any questions, just ask!
I would be very happy if I could overtake and maintain this awesome project!

Well... I hope that anyone replies!
~ Skayo

url_for using &amp; as separator

Hi,
First - thanks for making this great piece of code publicly available.

I've had some issues with the generated url from redirect_to(), which uses url_for().
The problem seems to be that & and not & is used as separator in the query string as per: https://github.com/sofadesign/limonade/blob/master/lib/limonade.php#L1651

When changing that row to use a plain & it seems to work just fine.
I guess there's a reason for this being & instead of &, and it's probably me that is doing something wrong - but I thought I should at least ask :)

Cheers,
Johan

autoload_controller($callback)

The issue is I can not send urls like redi-admin/post to the admin directory

function autoload_controller($callback)
{
$path = option('controllers_dir');
if(strpos($callback, "redi-admin") === 0) $path = file_path($path, 'redi-admin');
require_once_dir($path);
}

i have tried

function autoload_controller($callback)
{
$path = option('controllers_dir');
if(strpos($callback, "redi-admin/**") === 0) $path = file_path($path, 'redi-admin');
require_once_dir($path);
}

but that did not work

Risk of Cross-Site Request Forgery through _method POST parameter

The _method POST parameter can be used to override the HTTP method. This means that DELETE, PUT routes can be triggered through CSRF. The documentation should warn that when using libmonade these routes should be CSRF-protected as well.

Additionaly it might be useful to be able to disable this feature.

if uri starts with numbers

I need away to direct a URI that has been requested that starts with numbers must be a min of 3 numbers at start to go and load a different render

currently I use to do it by

dispatch_get("/*", array($redi, 'hello'));

and then get the home function to search the URI if it had numbers in it to then re-direct it somewhere else, however since I am re-ciding it all I thought I would ask if there is a better way to do such a function.

example
http://example.com/152A3
should go to
displatch_get("/:tagid",array($redi,"tagid"));

we use to use this function

public function hello(){

    if(is_numeric(substr(params(0), 0, 5)))
    {
            include_once("./plugins/tagID/index.php");  
            return render("p_tag");
    }

}

Middleware for router

Hi,

I wanted to implement SEO friendly URL's in my project and was wondering if there is a way using which I could create a middleware to map URL's to controllers dynamically.

TIA

Add possible result from template

Allow the templates to provide data to the layout. Extremely handy when you want to add stuff to header etc. See example at my fork github.com/dyve/limonade.

I also did a change to render that makes it somewhat smarter towards the $layout parameter.

Support for special characters in dispached regular expressions

I needed to match urls containing characters like "ñ" and this was not possible due to the urlencoding. I realized that this was done to secure in a way the values of the resulting params.

dispatch("^/test/(\d+)/{0,1}([a-zA-Zñ]*)", 'main');

Wouldn't match "/test/5/español" as it should (the actual url was more variable and complex, this is the simplified version). I don't know if its the best solution, but I solved it while mantining the previous behaviour for params in this commit: dresb/limonade@a54d79a419db71593d33d68fa6627cce0492aeb0

The majority of the changes in that commit are the text editor removing spaces at the end of the lines. The only two real changes are in the lines 1254 and 1272.

What version of PHP?

I'm wondering what is the minimum supported version of PHP supported by Limonade? It certainly works on 5.2.17, but I was interested in getting it working in a positively ancient 5.1. Is it likely to work or are major incompatibilities likely?

At any rate, it is probably worth putting the requirements in the readme.

Thanks again for making such an awesome framework.

option('views_dir' ... does not work

option('views_dir', dirname(FILE).'/other/dir/for/views');

the above when executed works only for an instant,

but it(the set value) later gets updated to the default value, by the limonade framework.

try this,

php 4 compatibility

Hi!
Thank you for this very nice piece of software.

I happen to have to work with an extremely annoying php 4.4.2 installation on the development server at work, and there were a few minor incompatibilities I experienced (mostly missing functions). So here is what I needed to add, in order to make the error messaged disappear.

// prevent error message in php < 4.3.2
// somehow this function was missing in my php installation
if(!function_exists('memory_get_usage'))
{
    function memory_get_usage()
    {
        return 0;
    }
}
// prevent error message in php < 5.1
if( !function_exists('headers_list'))
{
    function headers_list()
    {
       return array();
    }
}
// "backport" of php5
// from: http://www.php.net/manual/de/function.array-combine.php#82244
if( !function_exists('array_combine') )
{
    function array_combine($arr1, $arr2) {
        $out = array();

        $arr1 = array_values($arr1);
        $arr2 = array_values($arr2);

        foreach($arr1 as $key1 => $value1) {
            $out[(string)$value1] = $arr2[$key1];
        }

        return $out;
    }
}

Return functions returning a string...

When a template can't be found, the page renders the name of the view.

Is there a particular reason for this, or can we change it to throwing an error of some sort (at least in a development environment)

Route patterns optional parameters? (Updated to Request)

I apologise for asking this here but I didn't know where else to ask.

Can route patterns have an optional parameter for example a page number encoded as apart of the route?

For example to merge these two dispatches ..

dispatch('/blog/:slug','blog')
dispatch('/blog/:slug/:page', 'blog')

If so please can an example be added to the documentation regarding route patterns and parameters?

GET arguments override URL path?

I wonder if I'm missing something as I can't be the first person to notice this, but I have a case where I need to use GET arguments, and they don't appear to be working. The best way to show the problem is by example:

http://www.example.com/login

. . . loads the route for /login fine. But when I use this URL:

http://www.example.com/login?return=/cart

I get a 404 for "(GET) /return".

I dug into it and I see where request_uri tries to get the intended URL from PATH_INFO first, then tries to parse it from the query string, then checks REQUEST_URI. So when there's a query string, it never gets to REQUEST_URI .

AFAIK under Linux/Apache, the URL is always available in REQUEST_URI. So the following fix worked for me (I don't know if it would break things on other servers). I replaced lines 1046-1069 with the following:

    elseif(array_key_exists('REQUEST_URI', $env['SERVER']) && !empty($env['SERVER']['REQUEST_URI']))
    {
      $request_uri = preg_replace('/\?.+/' , '', rawurldecode($env['SERVER']['REQUEST_URI']));
      $request_uri = rtrim($request_uri, '/') .'/';
      $base_path = $env['SERVER']['SCRIPT_NAME'];
      if($request_uri."index.php" == $base_path) $request_uri .= "index.php";
      $uri = str_replace($base_path, '', $request_uri);
    }
    elseif (trim($query_string, '/') != '')
    {
      $uri = $query_string;
      $get = $env['GET'];
      if(count($get) > 0)
      {
        # exclude GET params
        $keys  = array_keys($get);
        $first = array_shift($keys);
        if(strpos($query_string, $first) === 0) $uri = $first;
      }
    }

Basically, I just switched the elseif blocks around and changed the way that $request_uri was being parsed.

Downloads page is empty

The Limonade site refers to this downloads page for the stable release, but there are no download links there.

make flash() available on same request?

I am having trouble using the flash().

I have a form, and in the POST route dispatch function, I validate the form. If it is invalid, I call flash() and render my view, but first the flash_now() is empty (as stated in the readme, I know).

But isn't it usefull to have it right away? Because now I need to set('error', 'some_message') instead,

Add optional xss protection

See : https://github.com/yesinteractive/fsl/blob/master/lib/fsl.php#L1136
Something like this…:

function request_uri($env=null)
{
   static $uri = null;
   if(is_null($env))
   {
      if(!is_null($uri)) return $uri;
      $env = env();
    }
   // retrieve $uri depending env
   $uri = rawurldecode(xss_filter($uri));  // store in static $uri variable
   return $uri;
}

if(!defined('xss_filter'))
{
   function xss_filter($str)
   {
        // minimal filtering like https://stackoverflow.com/a/1741568
   }
}

Poor performance in large projects

Since the routes are compiled into a pure REGEX manner, when reaching into the hundreds of routes the performance is severely degraded depending on where the route is. It has to run N regex patterns if the last route is hit. For large projects this is a severe stumbling block as I was reaching about 700-800ms with 1000 routes (testing) and that was just to figure out which route to run.

I toyed with the idea of storing the routes up to the first * or regex pattern to reduce the number of route lookups. For example:

route: /some/directory/*

$routes = array(
'some' => array(
'directory' => array(
'_routes' => array(
'ACTUAL REGEXP PATTERN' => $index_into_routes_array
)
)
)
);

This would allow you to do a couple quick lookups to possibly reduce the number of route lookups dramatically. Unfortunately I effed it up but something along these lines would help viability for using Limonade in large projects.

I could still make another attempt but perhaps there is a better way to handle this I'm not thinking of? Is there an existing methodology for handling a high number of directories with routes?

route handler fire twice

I use limonade with idiorm ( orm )

i have this route :

dispatch('/**', function(){
     $uid = params(0); 
    $data = appPloof::AntiSpam($_SERVER["REMOTE_ADDR"]);
    return $data;
});

and this is the AntiSpam function

public static function AntiSpam($ip){
    $ipM = ORM::for_table('antispam')->where_like("ip", $ip)->find_one();
    if(!$ipM){
        $ipM = ORM::for_table('antispam')->create();
        $ipM->ip = $ip;
        $ipM->nb = 0;
        $ipM->save();
        return true;
    }
    else {
        if($ipM->nb < appPloof::$limiteAntiSpam){
            $ipM->set_expr('nb', 'nb + 1');
            $ipM->save();
            return true;
        }
    }
    return false;
}

in my bdd i can see the nb is incremeted two by two !

before_render() with partials...

Hi,
I'd first like to thank you for the work you've done with this framework!

Second, is there a way to capture content sent to partials? I have a "header" partial that I need to transform its content before returning the value to the view...

thoughts?

Multiple directory for includes

Could be useful to be able to add more include folder for controllers, views, lib, etc.
Use case: Multiple apps (with multiple frontend controller (index.php...)) could load only the code it need + generic code (like, an app with the same layout as an other).
With this, it could be more simpler to share code between apps, too.

by exemple:

option('views_dir', dirname(FILE).'/views');

can become:

option('views_dir', array(dirname(FILE).'/views', dirname(FILE).'/myapp/views'));

here, /views is my genereic views folder, and /myapp/views is my app folder.

Thanks.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.