sofadesign/limonade

该软件包最新版本(dev-master)没有可用的许可信息。

一个 PHP 微型框架

dev-master 2023-10-13 07:10 UTC

This package is not auto-updated.

Last update: 2024-09-25 15:15:36 UTC


README

Limonade 是一个用于快速网络开发和原型设计的 PHP 微型框架。

它受到了 Ruby 中的类似 Sinatra 或 Camping 框架、Lua 中的 Orbit 框架的启发。它旨在简单、轻量且极其灵活。

Limonade 提供了补充 PHP 基本功能的函数,同时保持与原生函数的一致性并在此基础上进行扩展。

Limonade 易于学习,并提供了现代框架(MVC、REST 等)所能提供的一切。

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

关于本文件

本文件提供了 Limonade 功能的快速、全面指南。

如需更多信息,您可以查看 网站示例,以及当然还有源代码 lib/limonade.php,它仍然是最好的文档。

还有一个 讨论组,可用于更多交流。

要求

  • PHP 5.1.6 >(已成功测试 PHP 5.1.6,但可能适用于旧版本)

路由

路由结合

  • HTTP 方法
  • 与 URL 匹配模式
  • 和回调参数

因此,它们在 URL + HTTP 方法与回调控制器中提供的代码之间充当粘合剂。

    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
        }

路由按声明的顺序进行匹配。搜索通过浏览器 URL 中提供的路径进行。

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

当不支持 PUTDELETEPATCH 方法(如 HTML 表单提交)时,您可以在 POST 请求中使用 _method 参数:它将覆盖 POST 方法。

    <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>

路由模式和参数

模式可以包含命名参数。这些参数的关联值可以通过 params() 函数获得。

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

模式也可以包含通配符参数。关联值可以通过数字索引获得,其顺序与模式中的顺序相同。

    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;
            # ...
        }

与简单的通配符字符 * 不同,双通配符字符 ** 指定一个可能包含 / 的字符串。

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

如果模式以 ^ 开头,则模式也可以是正则表达式。

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

通配符参数和正则表达式也可以命名。

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

您还可以提供默认参数值,这些值将与模式参数合并并覆盖它们。

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

回调控制器

回调可以是函数、对象方法、静态方法或闭包。有关回调伪类型的更多信息,请参阅 php 文档

    # 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!';
    });

回调控制器返回渲染的视图输出(请参阅 视图和模板)。

它们可以将模式参数作为参数传递。

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

路由调用的回调可以在 run() 函数执行之前在任何地方编写。它们也可以分组在存储在 controllers/ 文件夹中的控制器文件中。

/                   # 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()...

此文件夹位置可以通过 controllers_dir 选项设置。

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

您还可以定义 autoload_controller 函数以以自己的方式加载控制器

    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重写

从版本0.4.1开始,Limonade支持URL重写。

如果您使用Apache,在您的应用程序文件夹中创建一个 .htaccess

<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>

如果您使用Nginx,请将以下内容添加到您的服务器声明中

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

然后请记住在您的 configure() 函数中明确设置 option('base_uri')

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

您可以使用类似 http://your.new-website.com/my/limonade/path 的URL访问您的网站,而不是 http://your.new-website.com/?/my/limonade/path

视图和模板

模板文件默认位于 views/ 文件夹中。视图文件夹位置可以通过 views_dir 选项设置。

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

要向模板传递变量,我们使用 set () 函数

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

变量也可以直接传递

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

set_or_default 函数允许传递变量,如果它为空,则传递默认值。这对于使用 params() 函数从URL提取的可选参数的分配非常有用。

    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.
        }

如您所见,最终输出由您的控制器返回。因此,请记住在控制器中使用 return 关键字明确返回您的视图! (这个注释对Ruby开发者尤其有帮助 ;-) )

布局

模板可以嵌入到另一个模板中:布局。

布局可以通过 layout 函数设置

    layout('default_layout.php');

或者直接使用模板渲染函数

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

如果布局值为 null,则将不带任何布局进行渲染。

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

格式化字符串和内联模板

格式化字符串可以像使用 sprintf 一样使用

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

还可以提供一个函数名作为模板。通过这种方式,例如,我们可以生成一个单文件应用程序。

    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模板

html 函数与 render 的用法相同。一个标题指定了适当的HTTP Content-type (text/html) 和通过选项定义的编码设置(默认为utf8)。

    html('my_template.html.php');

XML模板

xml 函数与 render 的用法相同。一个标题指定了适当的HTTP Content-type (text/xml) 和通过选项定义的编码设置(默认为utf8)。

    xml('my_template.xml.php');

CSS模板

css 函数与 render 的用法相同。一个标题指定了适当的HTTP Content-type (text/css) 和通过选项定义的编码设置(默认为utf8)。

    css('screen.css.php');

JS模板

js 函数与 render 的用法相同。一个标题指定了适当的HTTP Content-type (application/javascript) 和通过选项定义的编码设置(默认为utf8)。

    js('app.js.php');

TXT模板

txt 函数与 render 的用法相同。一个标题指定了适当的HTTP Content-type (text/plain) 和通过选项定义的编码设置(默认为utf8)。

    txt('index.txt.php');

JSON模板

jsonjson_encode 函数用法相同,并返回一个包含值JSON表示的字符串。一个标题指定了适当的HTTP Content-type (application/x-javascript) 和通过选项定义的编码设置(默认为utf8)。

    json($my_data);

文件服务

render_file 函数可以直接将文件渲染到输出缓冲区。

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

一个标题根据文件扩展名指定适当的HTTP Content-type,对于文本文件,通过选项定义的编码设置(默认为utf8)。

输出被暂时存储,以便轻松处理大文件。

部分

partial 函数是一个用于无布局渲染的快捷方式。对于管理可重用块并将其保存在单独的文件中非常有用。

此代码

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

与以下相同

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

捕获

content_for 函数允许你在视图中捕获一段文本。然后,捕获的块将可用于布局。这对于管理布局区域(如侧边栏)或设置特定于视图的javascript或stylesheet文件非常有用。

例如,使用此布局

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

并在你的视图中

    <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(); ?>

渲染结果为

    <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>

以上示例在这篇教程中有详细说明。

使用捕获与部分,它将帮助你组织视图,并避免多次复制/粘贴相同的代码。

钩子和过滤器

Limonade 允许用户定义一些函数,以增强 Limonade 的行为以满足其自身需求。

其中一些,如 before 钩子和 after 过滤器被广泛使用,而其他一些则是用于可能需要良好理解 Limonade 内部的先进使用。

在请求之前

你可以定义一个在每次请求之前执行的 before 函数。这非常有用,可以定义默认布局或向模板传递常用变量。

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

当前匹配的路由也会传递给 before 函数,因此你可以对其进行测试。它是一个数组,由内部 route_find 函数返回,具有以下值

  • method(HTTP方法)
  • pattern(正则表达式模式)
  • names(参数名称)
  • callback(回调)
  • options(路由选项)
  • params(当前参数)

在请求之后

还提供了一个在每次请求之后执行的 after 输出过滤器。它可以将转换应用于输出(对于直接发送到输出缓冲区的 render_file 输出除外)。

    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;
    }

当前执行的路由也适用于 after 函数。

在渲染之前

你可以定义一个在渲染视图之前执行的 before_render 函数。

前三个参数与传递给 render 函数的参数相同

  • $content_or_func:视图字符串
  • $layout:当前布局路径
  • $locals:直接传递给 render 函数的变量

最后一个参数,$view_path 默认为 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 函数,使其根据当前匹配的路由进行自动渲染。如果控制器返回空输出,则将执行此函数。

    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);
    }

在此示例中,当调用 url / 时,执行 hello(),然后 autorender() 渲染匹配的 hello.html.php 视图。

在退出之前

如果你定义了 before_exit,它将在停止/退出过程开始时被调用(在 Limonade 应用程序终止时自动调用 stop_and_exit 函数)。

    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
    }

在发送头之前

你可以定义一个在 Limonade 发出 header() 调用之前被调用的 before_sending_header 函数。这样,你可以添加额外的头信息

    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");
        }
    }

注意:请小心不要从 before_sending_header() 函数中重复调用 send_header() 以造成循环!

配置

你可以定义一个在应用程序启动时执行(在 run 执行的开始)的 configure。你可以在其中定义选项,例如数据库连接...

    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'));
    }

包含在 option('lib_dir') 文件夹(默认为 lib/)中的 PHP 文件在执行 configure 后使用 require_once 加载。因此,您可以在这个文件夹中放置所有您的 PHP 库和函数,以便它们在应用程序启动时加载并可用。

选项

option 函数允许您定义和访问应用程序的选项。

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

如果没有指定选项名称,则返回所有设置的选项数组。

您可以使用它来管理 Limonade 选项和您应用程序中的自定义选项。

默认的 Limonade 选项具有以下值

    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

会话

默认情况下,会话会自动启动。然后您可以使用与 $_SESSION 数组相同的语法访问会话变量。

您可以使用 session 选项禁用会话。

查看代码片段示例

闪存

闪存是会话的特殊用途。闪存值仅在下一次请求中可用,之后将被删除。它在表单上引发错误或通知成功操作非常有用。

  • flash($name, $value...) 定义下一次请求的闪存
  • 在视图中,您可以使用 $flash 数组或 flash_now($name) 函数获取当前闪存值。

查看代码片段示例

辅助函数

有关所有可用的辅助函数的更多信息,请参阅源代码或 API。

url_for

您可以使用 url_for 函数渲染 limonade URL。无论您的应用程序安装在 Web 服务器的哪个文件夹中,它们都会从文档根目录中的任何文件夹生成。

    # 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

如果您想使用 URL 重写,则需要显式设置 base_uri 选项(默认为 /your_file_path/?

停止和错误处理

停止

您可以使用 halt 函数立即停止应用程序的执行。错误将由默认的 Limonade 错误处理器或您定义的错误处理器处理。

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

未找到

默认情况下,显示 not_found 错误输出函数并发送 404 NOT FOUND HTTP 标头。

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

要为该错误定义新的视图,您可以简单地声明一个 not_found 函数。

    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 错误输出函数并发送 500 内部服务器错误 HTTP 标头。

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

PHP 错误也会被捕获并发送到此错误处理器输出。

要为该错误定义新的视图,您可以简单地声明一个 server_error 函数。

    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('error_layout.php');
    error_layout(); // return 'error_layout.php'

错误处理

除了常见的 NOT_FOUNDSERVER_ERROR 错误显示外,Limonade 还可以将精确的错误重定向到您自己的函数。

    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 表示所有 HTTP 错误

    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 表示所有 PHP 错误(由 PHP 发送或用户通过 trigger_error 函数引发)。

其他有用的函数

Limonade 还提供了一套有用的函数,可以帮助您管理文件、HTTP 等。有关这些实用程序的更多信息,请参阅 源代码 中的 7. 工具 部分。

测试

[待办事项]

更多