abgit/myfw

本包最新版本(0.9.7)无可用许可证信息。

一个迷你 PHP 框架

0.9.7 2021-03-09 09:10 UTC

This package is auto-updated.

Last update: 2024-09-09 16:57:44 UTC


README

要使用 myfw,我们只需要

  • 在此处下载最新稳定版本 这里

  • 在我们的根目录添加一个 htaccess 文件

RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [QSA,L]
  • 包含 myfw.php,初始化并运行。
require 'myfw.php';

$app = new myfw();
   ...
$app->run();

环境

myfw 至少可以在两种环境中使用:Web 或 Cron。

Web 环境。 当在 Web 环境中使用时,我们需要添加 get()post()map() 方法,并在末尾添加一个 run()get()post()map() 方法需要至少两个参数:要匹配的 URL 和要执行的功能。

require 'myfw.php';

$app = new myfw();
$app->get( '/', function(){
  ...
});

$app->run();

Cron 环境。 当在 Cron 环境中使用时,我们只需要添加 cron() 方法。Cron 方法有两个参数,一个是在命令行执行 Cron 时传递的字符串,以及要执行的功能。

require 'myfw.php';

$app = new myfw();
$app->cron( 'somearg', function(){
  ...
});

$app->cron( 'somearg2', function(){
  ...
});

要执行上一个 somearg Cron,我们只需要加载文件并在命令行中指定 somearg/bin/usr/php file.php somearg

hello world

让我们创建一个简单的示例。从我们的模板目录加载一个模板文件,分配一个 URL 变量并显示。

require 'myfw.php';

// init and set templates directory
$app = new myfw();
$app->config( 'templates.path', __DIR__ . '/templates' );

// on root
$app->get( '/', function() use ($app){
    $app->render( 'mytemplate', array( 'str' => 'Hello world' ) );
});

$app->run();

我们需要通过使用 config() 告知 myfw 我们模板文件的位置,以便 render() 可以加载它们。

参数

当使用动态 URL 时,我们必须使用变量并在函数中传递它们。例如:根据 URL 显示新闻条目。

require 'myfw.php';

// init and set template directory
$app = new myfw();
$app->config( 'templates.path', __DIR__ . '/templates' );

$app->get( '/news/:id', function($id) use ($app){
    $app->render( 'mytemplate', array( 'str' => 'My news id is ' . $id ) );
});

$app->run();

由于 myfw 集成了 twig 模板引擎,对于之前的示例,我们需要一个 templates/mytemplate.tpl 文件

{{ str }}

条件

对于每个动态参数,我们必须添加一个模式(例如::id),以及一个函数参数(例如:function($id))。作为良好的实践,我们应该始终过滤这些参数以控制参数限制、SQL 注入和其他问题。myfw 有一个 setConditions() 方法,应该描述 所有 参数,并放置在所有操作之前。

require 'myfw.php';

$app = new myfw();
$app->setConditions( array( 'id'   => '[0-9]{1,5}',
                            'arg1' => '[0-9a-zA-Z]{3,50}',
                            'arg2' => 'a|b' ) );

$app->get( '/news/:id', function($id) use ($app){
    ...
});

$app->get( '/other/:arg1/:arg2', function($arg1,$arg2) use ($app){
    ...
});

$app->run();

作为替代,我们可以使用 conditions() 方法自定义每个操作。例如

$app->get( '/news/:id', function($id) use ($app){
    ...
})->conditions( array( 'id' => '[0-9]{1,5}' ) );

模式

大多数时候,我们至少有两个环境: 开发(当我们的应用程序在本地执行并启用了某些调试时)和 生产(当我们的应用程序在远程服务器上执行时)。这意味着我们需要根据我们的模式分配不同的配置选项。为此,我们只需要使用 configureMode() 预定义每个模式的配置选项,并分配模式。让我们预定义具有不同配置设置的 开发生产 模式

require 'myfw.php';

$app = new myfw();

// choose mode
$app->config( 'mode', 'production' );

$app->configureMode( 'production', function () use ($app) {
    $app->config( array(
        'log.enable' => true,
        'debug' => false
    ));
});

$app->configureMode( 'development', function () use ($app) {
    $app->config( array(
        'log.enable' => false,
        'debug' => true
    ));
});

...

$app->run();

定义要使用模式的方法至少有 3 种

  • 硬编码:这是之前的示例,其中我们硬编码模式: $app->config( 'mode', 'production' );;
  • 通过 Apache 环境变量:我们需要自定义我们的 apache 配置 并添加一个特殊的变量 SLIM_MODE 并分配模式值;这种方法不需要额外的 $app->config() 调用;
  • 通过 PHP 环境变量:我们需要自定义我们的 php.ini 配置并添加一个变量。可以具有任何名称,例如: app.mode = "development" 并将其添加到我们的代码中: $app->config( 'mode', get_cfg_var( 'app.mode' ) );;

有时创建具有相同前缀的URL操作是有用的。我们不必分别指定每个操作,可以将其分组。这有几个优点:代码变得更加简洁,我们可以创建影响所有子操作的全球功能。

$app = new myfw();

$app->group('/backend', function() use ($app){

        $app->get('/adduser/:id', function ($id){
          ...
        });

        $app->get('/showuser/:id', function ($id){
          ...
        });
});

$app->run();

表单

在我的fw中,创建表单和处理所有逻辑流程非常容易。我们只需要定义表单行为,myfw就会处理一切。我们应该

  • 创建表单元素、规则和(可选)过滤器;
  • 在表单首次显示时显示的默认值;
  • 表单提交且有效时执行的操作
require 'myfw.php';

$app = new myfw();
$app->map( '/contact', function() use ($app){
  
  $app->form()->addText( 'name', ... )
              ->addSelect( 'chooser', ... )
              ...
              ->addSubmit();
  

  $app->form()->setDefault( function(){
     return array( 'name' => ..., 'chooser' => ... );
  });

  $app->form()->onSubmittedAndValid( function() use ($app){
     $formvalues = $app->form()->getValues();
     // do something with $formvalues
  });
  
  $app->render( 'mytplfile', array( 'form' => $app->form() ) );
)->via( 'GET', 'POST' );

$app->run();

注意,我们不需要创建新的变量来处理表单对象,所有内部myfw功能都是动态处理的。表单对象具有许多可探索的功能。

数据库

myfw对数据库的处理非常特别。每个调用都被视为一个MySQL存储过程调用。没有硬编码的SQL,没有ORM。所有SQL代码都存储在MySQL数据库中,我们只需要调用一个过程。为什么是存储过程?来自 w3resouce.com

  • 存储过程速度快。MySQL服务器利用缓存的优势,就像预定义语句一样。主要速度提升来自减少网络流量。如果你有一个重复的任务,需要检查、循环、多个语句,并且没有用户交互,你可以通过调用存储在服务器上的单个过程来完成。
  • 存储过程是可移植的。当你用SQL编写存储过程时,你知道它将在MySQL运行的每个平台上运行,而不需要强制你安装额外的运行时环境包,或者设置操作系统中的程序执行权限,或者如果你有不同类型的计算机,则需要部署不同的包。这就是用SQL编写而不是用Java、C或PHP等外部语言编写的好处。
  • 存储过程始终作为数据库中的“源代码”可用。并且将数据与操作数据的进程关联起来是有意义的。
  • _ 存储过程是可迁移的!MySQL严格遵循SQL:2003标准。其他(DB2、Mimer)也遵循。
// load a news item. call findnewsid() mysql procedure
$app->db()->findOne( $result, 'findnewsid(nid|int)', array( 'nid' => $id ) );

我们只需要使用db(),因为我们只想检索一行,我们可以使用findone()方法来执行mysql findnewsid过程。因为此过程需要一个整数参数,我们必须描述它,包括参数名称nid和参数类型int,并在数组中包含我们的参数值列表。数组值键必须与参数名称定义匹配。db方法始终返回调用状态(如果调用已执行并且从数据库发送了有效结果,则为true,如果调用无法完成或从数据库发送了无效结果,则为false)。调用结果存储在作为第一个参数传递的$result变量中。

其他方法

示例:获取所有元素(选择)

// load all news. call findnews() mysql procedure
$app->db()->findAll( $results, 'findnews()' );

示例:更新或插入元素

$app->db()->query( 'newsinsert(id|int,title|str|255)', array( 'id' => $id, 'title' => $title ) );

组合db和表单对象

以下是如何将表单对象与db对象组合的简单示例。我们将创建一个表单,从数据库获取默认值,将db值分配给表单元素,如果表单提交且表单元素值有效,我们将更新数据库中的项目。

require 'myfw.php';

$app = new myfw();
$app->setConditions( array( 'id' => '[0-9]{1,5}' ) );

$app->map( '/item/:id', function($id) use ($app){

  $app->form()->addText( 'title', ... )->addSubmit();

  $app->form()->setDefault( function () use($app,$id){ 

        if( $app->db()->findOne( $result, 'findnewsid(nid|int)', array( 'nid' => $id )))
            return $result;        

        $app->form()->setErrorMessage( 'News item not available' )->hide();
  });

  $app->form()->onSubmittedAndValid( function() use ($app,$id){

        if( $app->db()->apply( 'newsitemupdate(nid|int,title|str|255)', array( 'nid' => $id, 'title' => $app->form()->getValue( 'title' ) ) )
            $app->form()->setSubmitMessage( 'News item details changed.' );
  });

  $app->render( ... );
)->via( 'GET', 'POST' );

i18n

国际化支持在PHP逻辑和模板侧处理。myfw使用PHP内置的gettext来处理翻译,在PHP和模板侧都使用__n两个函数。

主要目标是

  • 在PHP和模板环境中都有标准方式;
  • 与PoEdit字符串提取兼容。

php示例

  • 简单
 _( "hello world" );

例如:输出已翻译的"hello world"

  • 变量
 _n( "welcome %s to %s", array( $name, $portal ) );

例如:如果 $namedavid 并且 $portaldomain.com,则输出翻译后的 "欢迎david来到domain.com"

  • 单数/复数
 _n( "1 orange", "lots of oranges", $counter );

例如:如果 $counter1,则输出 "1个橙子",如果 $counter5,则输出 "很多橙子";

  • 带有变量的单数/复数翻译
_n( "1 orange", "%s oranges", $counter, $counter );

例如:如果 $counter1,则输出 "1个橙子",如果 $counter5,则输出 "5个橙子";

  • 根据上下文带有变量的单数/复数翻译
_n( "1 orange in %s tree", "%s oranges in %s trees", $counter, array( 'big' ), array( $counter, 'small' ) );

例如:如果 $counter1,则输出 "1个橙子在大的树上",如果 $counter5,则输出 "5个橙子在小的树上";

模板示例

  • 简单
_( "hello world" )
  • 简单的带变量
_n( "welcome %s to %s", [name, portal] )
  • 单数/复数
_n( "1 orange", "lots of oranges", counter )
  • 带有变量的单数/复数翻译
_n( "1 orange", "%s oranges", counter, counter )
  • 根据上下文带有变量的单数/复数翻译
_n( "1 orange in %s tree ", "%s oranges in %s trees", counter, ['big'], [counter, 'small'] )

配置

为了在myfw中初始化i18n支持,我们需要设置i18n并通过使用i18n的setPath()来设置翻译文件路径,对于我们的LC_MESSAGES/lang/*.mo文件,可选的还有domaincodeset。然后,我们只需要更改我们的动作中的语言。

$app = new myfw();
$app->i18n()->setPath( __DIR__ . '/../i18n' );

$app->get('/:lang/something', function( $lang ) use ($app){
    $app->i18n()->setLang( $lang );
    ..
});

i18n()也支持会话。这样我们就可以使用会话值(如果存在的话)

$app->i18n()->setLang( 'en_US', true, true );

在之前的示例中,我们更改语言为en_US,如果可用则使用会话值(而不是en_US),并将en_US保存到会话中供将来使用。

PoEdit设置

PoEdit是最好的开源软件之一,用于处理所有翻译事宜。其中一个最有用的特性是能够将字符串提取出来以计算*.po文件。我们只需要做的是

  1. 在Python设置中添加*.tpl扩展名。转到文件 > 首选项 > 选择Python > 编辑 > 将分隔的扩展名列表更改为:*.py;*.tpl
  • 创建一个新文件:文件 > 新建;或者打开一个现有文件:文件 > 打开;
  • 创建关键字:目录 > 属性... > 点击标签源关键字 > 添加_n:1,2并添加_n:1

规则

myfw有一个内置库来检查模式。这些只是一些简单的布尔方法,用于检查一个字符串参数并返回true/false。这些方法在添加表单元素时与表单对象集成,这样我们就可以检查它的值,但我们也可以单独使用它们。

表单集成

$app->map( '/contact', function() use ($app){
  
  $app->form()->addText( 'myelement', 'Label', array( 'required' => 'element is required' ) )
              ...
              ->addSubmit();
  ...
)->via( 'GET', 'POST' );

$app->run();

在添加表单元素时,我们可以将规则数组作为第三个元素分配。这是一个数组,其中每个键是规则名称(方法名称),值是一个包含消息和附加选项的数组。

$app->map( '/contact', function() use ($app){
  
  $app->form()->addText( 'myelement', 'Label', array( 'required' => 'element is required' ) )
              ->addText( 'mydescription', 'Description', array( 'required' => 'Descrition is required', maxlen => array( 'Description is too big', 200 ) ) )
              ...
              ->addSubmit();
  ...
)->via( 'GET', 'POST' );

$app->run();

独立

if( $app->rules()->maxlen( $x, 250 ) ){
  ...
}

myfw 支持的规则

  • 必需
  • 数字
  • 最大长度
  • 正则表达式
  • 电子邮件
  • 非电子邮件
  • 金钱
  • IP
  • MD5
  • 标签
  • 仅字母
  • 字符
  • 最大超链接数
  • 最大长度
  • 最小长度
  • 无标点符号
  • 字母数字
  • 严格字母数字
  • 验证码
  • 选择有效
  • 匹配字段
  • 不匹配字段
  • 字段必需
  • HTTP URL
  • 域名
  • 子域名
  • 十六进制颜色

过滤器

与规则库类似,过滤器库可以在三个环境中使用:表单、模板和独立。

表单过滤器

在添加表单元素时。如果表单提交有效,则检索值时将过滤元素值。

// add form elements
$app->form()->addText( 'myelement', 'Label', array(..), array( 'trim' ) );

$app->form()->onSubmittedAndValid( function() use ($app){

  // get values (filtered)
  $arr = $app->form()->getValues();
});

模板过滤器

myfilters库中所有可用的过滤器以及原生twig过滤器都可以在模板中使用。

native upper filter
{{ 'welcome'|upper }}

myfw md5 filter
{{ 'welcome'|md5 }}

独立过滤器

过滤器库可以直接在PHP中调用,也可以作为独立使用。

$filteredvalue = $app->filters()->gravatar( 'someemail' );

可用的过滤器:

  • 绝对值

  • 批处理

  • 首字母大写

  • 转换编码

  • 日期

  • 修改日期

  • 默认值

  • 转义

  • 第一个

  • 格式化

  • 连接

  • JSON编码

  • 最后一个

  • 长度

  • 小写

  • 换行符转换为换行

  • 数字格式化

  • 合并

  • 大写

  • 原始数据

  • 替换

  • 反转

  • 四舍五入

  • 切片

  • 排序

  • 分割

  • 删除标签

  • 标题化

  • 删除空格

  • URL编码

  • 删除空格

  • SHA1

  • MD5

  • 换行符转换为换行

  • 浮点值

  • 整数值

  • 缩短

  • 十六进制颜色

  • 状态颜色

  • 扩展

  • 订单

  • t

  • m

  • rnumber

  • bcloudname

  • gravatar

  • url

  • 域名

  • markdown

  • 之前

  • xss

会话

会话库包含处理php $_SESSION 的简单方法。我们应使用这个简单的库来存储、删除、获取会话信息,而不是直接访问。

// set $key 
$app->session()->set( $key, $label );

// set $key if not exists only
$app->session()->setcheck( $key, $label ); 

// get $key value
$k = $app->session()->get( $key );

// check if $key exists
if( $app->session()->exists( $key ) ){ .. }

// delete $key
if( $app->session()->delete( $key ) ){ .. }

// set pluralkey 'admin - x' ( same as $_SESSION[ 'admin' ][ 'x' ] )
$app->session()->set( array( 'admin', 'x' ), $valueA );
$app->session()->set( array( 'admin', 'y' ), $valueB );

// get pluralkey 'admin - x'
$valueA = $app->session()->get( array( 'admin', 'x' ) );
$valueB = $app->session()->get( array( 'admin', 'y' ) );

缓存

这是一个处理apc或redis操作的简单库。在访问、设置或删除键之前,请确保使用$app->config()设置环境。

// default apc ti to leave
$app->config( 'apc.ttl', 600 );

// default redis time to leave
$app->config( 'redis.ttl', 600 );

// cache set using apc
$app->cache()->set( APP_CACHEAPC, $key, $value );
or
$app->cache()->apcset( $key, $value );

// cache set using redis
$app->cache()->set( APP_CACHEREDIS, $key, $value );
or
$app->cache()->redisset( $key, $value );

// cache apc delete
$app->cache()->delete( APP_CACHEAPC, $key );

// cache redis delete
$app->cache()->delete( APP_CACHEREDIS, $key );

缓存引擎具有内置的中间件架构来缓存操作。如果我们有几乎静态的动作,我们可以激活iscache,这样就可以显示缓存的版本。

require 'myfw.php';

$app = new myfw();
...

$app->get( '/news/:id', 'iscache', function($id) use ($app){
    ...
    $app->render( 'sometemplate', array( .. ) );
});
...

在先前的例子中,如果存在/news/:id,则将显示所有HTML,不会进行任何额外的处理。如果缓存为空或第一次,则render()将自动渲染模板并存储在缓存中,以便下次调用/news/:id时显示缓存的版本。注意,如果

  • 服务器检测到apc引擎;
  • 是HTTP请求;
  • 在动作中没有创建任何$app->form()

购物车

购物车库基于会话库处理简单的购物车功能。有一些可用的方法,如getItemsgetTotalItemsgetTotaladdItemisItemisItemValueaddExtragetExtraremoveItemupdateItemPropertygetItemPropertyValuecheckItemPropertyclear

日志记录

当我们需要检查应用程序内部行为时,日志记录很有用。在您的配置中设置日志级别,然后进行日志记录。

$app->config( 'log.init', true );
$app->config( 'log.level', \Slim\Log::DEBUG );

$app->cron( 'somecron', function() use ($app){
    ...
    $app->log()->debug( 'my critical log line' );
    ...
    $app->log()->warning( 'my warning log line' );
});

目前,日志使用内部php print 输出,这在使用cron环境时很有用。

邮件器

邮件引擎是一个用于发送邮件的非常简单的库。此库与rackspace mailgun api集成以进行投递,因此需要一些额外的config()参数。

// default params
$app->config( 'email.from', $from );
$app->config( 'email.to', $to );
$app->config( 'email.subject', $subject );

// mailgun specific
$app->config( 'email.mailgunkey', $key );
$app->config( 'email.mailgundomain', $domain );

// send email
$app->mailer()->send( $from, $to, $subject, $text );

// send simple email with default email.from config
$app->mailer()->sendsystem( $to, $subject, $text );

// send simple email with default email.from, email.to and email.subject config
$app->mailer()->sendinternal( $text );

HTML支持

邮件引擎支持模板。这意味着我们可以将模板分配给邮件库,这样当我们发送电子邮件时,就会根据该HTML模板计算电子邮件。

我们需要在模板目录中创建一个模板文件,添加一个content标签,该标签将由邮件引擎注入消息,并通知邮件器模板文件名。

mymail.tpl文件

some header
  {{content}}
some footer
$app->config( 'email.template', 'mymail' );

邮件头

如果我们需要指定额外的邮件头,我们有一个配置参数用于此目的。如果需要添加“bcc”头,则很有用。

$app->config( 'email.headers', array( 'bcc' => 'someemail' ) );

urlFor

urlFor()是创建应用程序URL的方法,这样您就可以自由更改路由模式,而不会破坏您的应用程序。此方法可以在PHP环境(这是原生slim方法)或模板环境中使用。

只需为每个动作添加一个名称。

$app = new myfw();
$app->get('/new/:name/go', function ($name) {
    echo "Some news item $name!";
})->name('newitem');

PHP环境示例

$url = $app->urlFor( "newsitem", array( "name" => $someitem ) );

模板环境示例

<a href="{{ urlFor( 'newsitem', {'name':someitem} ) }}">

例如,如果someitem变量包含xpto,则两个环境都会计算/new/xpto/go URL。

病毒总览

病毒总览是谷歌基于某些杀毒分类检查文件和网站的谷歌云服务。要使用此库,我们必须设置病毒总览API密钥并使用getInfo方法分配网站信息。

// setup virustotal key
$app->config( 'vtotal.api', $key );

if( $app->vtotal()->getInfo( $info, "domain.com" ) ){
   // read $info
   ... 
}

transloadit

这是一个tranloadit云服务的集成库。我们只需要设置凭据,使用createAssembly()创建一个assembly,或者使用request()请求assembly状态信息。

// setup key and secret
$app->config( 'transloadit.k', $key );
$app->config( 'transloadit.s', $secret );

// compute transloadit assembly
$assembly = array(
    'params' => array(
        'template_id' => 'abababababababababababababa',
        'steps'       => array(
                'mystep' => array( 
                     'param' => $paramvar,
                      ),
                'otherstep' => array( 
                     'otherparam' => $otherparamvar
                      )
                )
             )
     );

// transloadit assembly webservice call
if( $app->transloadit()->createAssembly( $result, $assembly ) ){
 ...
}

Transloadit createAssembly()只是初始化assembly。然后,transloadit云服务将处理它,我们需要在一段时间后检索结果。为此,我们需要使用request()与我们在createAssembly中检索的assembly URL,即$result

if( $app->transloadit()->request( $returninfo, $assemblyUrl ) ){
    // do something with $resultinfo
}

brightcloud

Brightcloud是一个用于分类域的云服务。目前库检索一个域的信息。我们需要设置凭据并在getInfo()方法中提供信息。

$app->config( 'bcloud.k', $key );
$app->config( 'bcloud.s', $secret );

if( $app->bcloud()->getInfo( $result, "domain.com" ) ){
    // do something with $result
}

--