Tero是一个强大的框架

安装: 29

依赖项: 0

建议者: 0

安全: 0

星标: 0

关注者: 1

分支: 2

开放问题: 0

语言:HTML

类型:项目

1.0.2 2019-12-10 22:14 UTC

This package is not auto-updated.

Last update: 2024-10-02 12:21:24 UTC


README

Tero

TERO 框架

Tero是一个针对PHP的微型Web框架,旨在简化代码编写和快速解决问题。





关于Tero

Tero是用php 5.3编写的(支持使用匿名函数和bind),旨在运行该版本以及服务器版本5.6和7+。

是否使用友好URL是可选的,但如果要使用它,则应在apache中启用mod_rewrite。

数据库方面,通过PDO几乎可以集成任何数据库,Tero在MySQL 5.5 / Mariadb / SQLite / Mssql Server 2005+中被广泛使用。

支持Windows(wamp)和Linux(lamp)基础服务器。

要安装Tero,只需在控制台导航到网站目录并运行composer即可。

安装

composer create-project dromero86/tero project_name

Tero默认具有以下文件夹结构

   MyWebsite/
	|--app/
	|  |--config/
	|  |--library/
	|  |--model/
	|  |--schema/
	|  |--third_party/
	|  |--vendor/
	|
	|--ui/
	|  |--images/
	|  |--themes/
	|     |--mytheme
	|
	|--index.php

index.php作为引导程序来执行核心,它有两个任务,第一个是加载项目所需的所有库,第二个是加载用户编写的所有模型-控制器,并最终执行由URL要求的控制器。

app文件夹具有框架的非公开结构,因此无法通过Web访问,而ui文件夹包含所有将用于Web的资源,如图像、脚本和CSS文件。

Hello World with Tero是

//1#
<?php if ( !defined('BASEPATH')) exit('No direct script access allowed');

//2# 
$App = core::getInstance();  

//3# 
$App->get("index", function()
{    
    //4# 
    echo "Hello world!";
});

第一行指定了Tero的环境(类似于codeigniter),对所有用户定义的文件都有效。这是一个安全措施,不要直接访问文件并利用漏洞。

第二行获取核心实例,这对于定义我们的控制器和访问定义的库/助手非常重要。

第三行定义我们的控制器,第一个参数将解析Web调用,而函数将解析要返回的内容。

第四行是浏览器打印的内容。例如,如果我们的项目位于https:///MyWebsite/,则此驱动程序将返回的内容是

Hello World!

请注意,使用"index"表示默认控制器,因此无需向URL添加参数。

命令行模式

php /path/to/tero/index.php action="my-command" arg_name1="arg_value1" arg_nameN="arg_valueN"

设置应用程序

要包含库或助手,使用core.json,该文件包含加载配置和其他选项以改进或限制应用程序的性能,该文件具有以下语法

{
    "loader":
    [
        {"file":"app/vendor/Parser"    , "library":{ "class":"Parser"   , "rename":"parser" } },
        {"file":"app/vendor/Skeleton"  , "library":{ "class":"Skeleton" , "rename":"view"   } },
        {"file":"app/vendor/Dataset"   , "library":{ "class":"Dataset"  , "rename":"data"   } },
        {"file":"app/vendor/input"     , "library":{ "class":"input"    , "rename":"input"  } },
        {"file":"app/model/docs"       , "helper" : true },
        {"file":"app/model/sketch"     , "helper" : true }  
    ],
    "debug"   : false,
    "error"   : "On" ,
    "leak"    : "50M",
    "timezone":"America/Vancouver",
    "encoding": "UTF-8" 
}

在加载器中,堆叠要加载的元素,加载顺序是从升序到降序,以“从最小的依赖关系开始”为标准。

"File"属性表示文件的位置,从文件所在的文件夹开始到文件名(不包含".php")。

区分类或库和助手很重要,因为核心将类的实例作为公共属性保存(在用户驱动程序中可访问),然而,对于助手,仅加载它。

对于库,我可以重命名实例,例如

$App->get("...", function(...))
{
     //data is rename of dataset
     $this->data->set(...);
});

路由URL

为了获取URL参数作为参数并理解处理过程,我们将解释它们是如何工作的。

Tero接受(其中"index"是示例方法)

  • /?action=index
  • /index
  • /index-:id => :id是参数
  • /index?another=param
  • /index-:id?another=param

方法URL:INDEX 参数顺序:从左到右

按此顺序提取URL方法

  • 简单匹配:在重写的URL中找到方法,不使用正则表达式URL,如果找不到
  • 参数匹配:在重写的URL中使用正则表达式找到方法,如果找不到
  • 默认:解析GET参数

如果方法存在且可调用,调用它,否则调用默认方法“index”

要在Apache中处理友好的URL,请确保启用“mod_rewrite”,并包含 .htaccess 文件

RewriteEngine On

RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f

RewriteRule ^ index.php [QSA,L]

这将启用类型URL /my-cool-page 或参数化URL /product/:name-:id,其中 ":name" 和 ":id" 是将用于方法中的参数

https:///myterocart/product/cool-glass-red-2345

$App->get("product/:name-:id", function($name, $id)) //name unnused in the example
{
     //take params
     $id = (int)$id; 

     $this->db->query(“SELECT price FROM products WHERE product_id = {$id}”);
});

稍后我们将看到参数的更多用法。

重定向

我们经常需要从服务器跳转到另一个页面,这可以通过实现如下原生的“redirect”函数来解决

$App->get("product/:id", function($id)) 
{
	$category = 36;
	redirect(“products/category/{$category}”);
  ...

在示例中,这意味着您将从产品页面跳转到分类页面,此函数还允许您在执行永久重定向时指定头信息。

请求

捕获URL的GET变量

A) 简单的原生路径:GET参数被序列化并按如下方式传递给模型函数

//https:///museum_library/?action=download_pdf&file=myfile.pdf

$App->get("download_pdf", function($file)) 
{
...

这里处理变量是file,请注意“action”是一个保留字,由Tero核心内部使用。

我们还必须理解,Tero以字符串形式返回所有参数,我们负责进行适当的转换以避免安全漏洞。

B) 使用友好URL:参数是通过在URL模式上使用正则表达式获得的(不使用GET),如我们之前看到的示例

//https:///terocart/product/36
$App->get("product/:id", function($id)) 
{
...

这里将获取"product/36",在这种情况下,将获取"36"并将其作为方法的参数传递。

C) 稍微复杂一些(但很有趣):Tero可以将这两者结合起来,一方面通过正则表达式提取变量,另一方面处理GET参数,留下一个有趣的怪物

//https:///clothestero/tshirt-category/36?color=blue&size=xxl
$App->get("tshirt-category/:category, function($category, $color=””, $size=””)) 
{
...

为了让我们的Frankenstein工作,我们必须考虑一些事情

  1. URL模式先于
  2. GET参数不是按照正则表达式的形式编写的
  3. GET参数可以使用PHP语法作为可选参数来放置 {$ variable = ""}
  4. 当我们使用GET时,我们必须尊重参数的顺序

使用POST

POST的使用不是Tero核心的本地功能,但如果将其作为库包含,则必须将其包含在core.json中

{
    "loader":
    [
        ...
        {"file":"app/vendor/input" , "library":{ "class":"input" , "rename":"input"  } }

它的使用非常简单

$App->get("...”, function(...)) 
{

   $post = $this->input->post();
   ...

post()输入方法将_POST数组转换为对象(并处理元素),作为对象,它在以后使用数据库时将很有用,即使我们不需要它进行数据库操作,其使用也非常方便

	$post = $this->input->post();
	
	$post->name 
	$post->phone
	etc

检查是否有POST元素

$App->get("...”, function(...)) 
{
   if($this->input->has_post())
   {
     $post = $this->input->post();
   }

设置数据库

要集成我们的数据库,我们必须首先从core.json中启用它,这如下所示

{
    "loader":
    [
      ...
      {"file":"app/vendor/database", "library":{ "class":"database" , "rename":"db"  } 

这表示我们将通过核心属性"$this->db"访问数据库,但我们仍然需要配置数据库访问,为此我们将不得不编辑db.json

{
    "database" :
    {
        "driver"    : "mysqli",
        "user"      : "mydbuser"      ,
        "pass"      : "mydbpass"          ,
        "host"      : "myhostname" ,
        "db"        : "mydbname"        ,
        "charset"   : "utf8"      ,
        "collate"   : "utf8_general_ci",
        "debug"     : false
    }
}

如果一切正常,我们可以检查连接是否工作,如下所示

$App->get("...”, function(...)) 
{
   var_dump($this->db->is_ready());

如果返回TRUE,则表示我们成功连接。

支持多数据库连接

将默认值设置为TRUE以激活数据库

{
    "server1" :
    {
        "driver"    : "mysqli",
        "user"      : "mydbuser"      ,
        "pass"      : "mydbpass"          ,
        "host"      : "myhostname" ,
        "db"        : "mydbname"        ,
        "charset"   : "utf8"      ,
        "collate"   : "utf8_general_ci",
        "debug"     : false,
        "default"   : true
    },
    "server2" :
    {
        "driver"    : "mysql",
        "user"      : "mydbuser"      ,
        "pass"      : "mydbpass"          ,
        "host"      : "myhostname" ,
        "db"        : "mydbname"        ,
        "charset"   : "utf8"      ,
        "collate"   : "utf8_general_ci",
        "debug"     : false
    },
    "server3" :
    {
        "driver"    : "mssql",
        "user"      : "mydbuser"      ,
        "pass"      : "mydbpass"          ,
        "host"      : "myhostname" ,
        "db"        : "mydbname"        ,
        "charset"   : "utf8"      ,
        "collate"   : "utf8_general_ci",
        "debug"     : false
    }
}

只需调用

$server2 = $this->db->use("server2");
$server2->query("...");

$server3 = $this->db->use("server3");
$server3->query("...");

查询和结果

要运行查询,只需输入

$rs = $this->db->query(“SELECT … FROM … ”);

其中$ rs将包含封装在对象中的结果信息,以便可以多次以下方式访问所需信息

$rs = $this->db->query(“SELECT id FROM … ”);

foreach($rs->result() as $row)
{
	//$row->id;
	//$row->{“id”}
}

如果您想运行存储过程,可以按如下方式执行

$rs = $this->db->procedure(“mysp(1,’2’)”);

foreach($rs->result() as $row)
{
   ...
}

重要提示:要在MySQL中与存储过程一起工作,必须在db.json中配置驱动程序为mysqli

活动会话

我们可以使用 Telepatia 库来处理会话。要加载它,我们必须按照以下方式编辑 core.json。

{
    "loader":
    [
      
      {"file":"app/vendor/database", "library":{ "class":"database" , "rename":"db"  } 
      {"file":"app/vendor/Telepatia", "library":{ "class":"Telepatia" , "rename":"sesion"  } 

为了使会话正常工作,我们必须理解这些会话存储在数据库中,因此,在 Telepatia 之前需要加载数据库库。完成此操作后,我们必须配置 sesion.json

{
    "Telepatia": 
    {
        “app”:”name_of_cookie_ref_app”,
        “table”:”db_session_table”,
        “timeout”: 60,
    }
}

要将变量存储在会话中,必须放置

//$variable ( string | id | float )
$this->session->send( $variable);

要恢复存储的数据

$value = $this->session->recv() ;

如果会话不活跃,$value 将等于 FALSE

要关闭会话,必须放置

$this->session->close();

WEB SKELETONS

Web skeletons 是由模板组成的页面,这些模板被组合在一起,然后与数据一起渲染。

要使用它,我们必须进行以下调整

在 core.json 中

{
    "loader":
    [
     {"file":"app/vendor/Parser", "library":{ "class":"Parser", "rename":"parser" } },
     {"file":"app/vendor/Skeleton", "library":{ "class":"Skeleton","rename":"view”} }, 

解析器,在低级别,用于替换变量 "{my_variable}" 中的值。

然后我们必须定义我们的 theme.json

{
    "path": "ui/themes/my_theme/",
    "vars": "vars.json",
    "view": "view.json"
}

在 my_theme 文件夹内,我们必须创建 2 个文件:vars.json 用于静态变量和 view.json,它将包含页面的结构。my_theme 文件夹建议的结构可以是

   MyTheme/
	|--css/
	|--fonts/
	|--img/
	|--js/
	|--css/ 
	|
	|--views/
	|  |--blocks/
	|     |--_home.php
	|     |--_product.php
	|     |--_contact.php
	|  |--emails/
	|  |--global/
	|     |--_layout.php
	|     |--_header.php
	|     |--_foorer.php
	|
	|--vars.json
	|--view.json

您可以看到,我们创建了 3 个全局文件:layout、header 和 footer,以及 3 个对应于可以(或不可以)重复的部分的文件。

现在,我们暂时让 vars.json 保持为 {},并专注于 view.json

在 view.json 中创建一个页面

{
	"index" :
	{ 
        "layout" : "global/_layout.php",
        "header" : "global/_header.php",
        "content":  
        [
            { "file": "block/home.php" }, 
        ],  
        "footer" : "global/_footer.php"
	},

我们可以从控制器这样调用它

$App->get("index", function())  
{ 
     $this->view->write(“index”);
});

这将返回网页。

现在,它是如何工作的?里面到底发生了什么?

一切始于 Parser 库,这是 codeigniter 用来处理模板的绝妙想法,要查看其使用,请参阅:https://www.codeigniter.com/userguide3/libraries/parser.html 理念非常简单,就是用 "$ variable_php_usually_string_o_entero" 替换 "{variable_en_plantilla_html}"。

Tero 将这个概念提升到一个新的高度,并将其转化为视图布局的原则,这个新原则可以这样表达。

> "The HTML view will not under any circumstances have PHP code, its use will be penalized with imprisonment in maximum security jail (nah !, lie;))".

这给我们的 PHP 程序员朋友带来一个问题,但不要惊慌,我们这样解决。

  1. 理解 Parser 渲染的强大功能,允许我们连接简单的变量,以后这将允许我们连接我们的数据结果集。
  2. 理解列表渲染允许我们编写选择性的代码,例如
<div>
{si_tiene_session_activa}
	<h1>Session id: {si_tiene_session_activa_username}</h1>
{/si_tiene_session_activa}
</div>

而我的服务器代码可以是

$data[“si_tiene_session_activa”]= array();

$sesion = $this->session->recv() ;

if(!$sesion) // != FALSE
{
$data[“si_tiene_session_activa”][]=array
(
“si_tiene_session_activa_username”=>$session
);
}

$this->view->write(“myview”, $data);

稍后,我们可以这样做,只写一行。

因此,好处显而易见;)

  1. 模板分段,所有可以在块中完成的事情都可以被重用。
  2. 简化布局,使用简单的变量进行放置和使用
  3. 易于调试,如果您看到变量 "{sin_renderizar}",则表示出了问题。
  4. 没有 PHP 错误,如果没有嵌入 PHP 代码,就没有错误的可能性。
  5. 对 JavaScript 友好,这是区别。
  6. 易于设计师和布局设计师思考(经证明)

数据集

数据与视图之间的管道。

为了最小化 Parser 接收的数组产生的多余代码,为了解决将数据与视图连接产生的复杂性,并反映一种可维护的易读代码,数据集存在。

将其包含到 core.json 中

{
    "loader":
    [
      …
      {"file":"app/vendor/Dataset", "library":{ "class":"Dataset" , "rename":"data"  } }

如何使用?

设置一个变量

$this->data->set(“username”, “mynickname”);

或者

$this->data->set(“username”, $variable);

如果我们想定义具有此结构的数据列表

{news}
<h1>{news_title}</h1>
<p>{news_details}</p>
{/news}

我们必须以这种方式开始一个列表

$this->data->set(“news”);

要填充它,我们可以映射一个对象

$news = new stdclass;
$news->title = “A example  news”;
$news->details = “this is a example news row”;

$this->data->map(“news”, $news);

$this->view->write(“myview”, $this->data->get());

这将产生

<h1>A example  news</h1>
<p>this is a example news row</p>

现在,这并不是为了 stdclass 的使用,那么 ... 它是做什么的?

数据库!

看看这是如何做的

$this->data->set(“news”);

$rs = $this->db->query(“SELECT title,details FROM news_table”);

foreach($rs->result() as $row)
	$this->data->map(“news”, $row);

$this->view->write(“myview”, $this->data->get());

仅用 5 行代码就完成了以下操作

  1. 填充了一个模板中的数据。
  2. 如果没有数据,变量将不可见
  3. 如果您的查询有更多字段,它们将自动渲染为 news_xxxx
  4. 您可以轻松更改变量的名称,使代码更易读

如果我不想有一个列表,或者我有一个单独的记录要渲染怎么办?

那么,您可以使用“automap”,其原理是取一个对象,并为每个属性创建一个单独的变量。

回到新闻示例...

$news = new stdclass;
$news->title = “A example  news”;
$news->details = “this is a example news row”;

$this->data->automap($news, “news_”);

$this->view->write(“myview”, $this->data->get());

这应该有助于我们填充这个视图

<h1>{news_title}</h1>
<p>{news_details}</p>

注意,没有任何涉及模板的列表,这是因为它们是单独的变量。

原则

  • Tero必须易于学习。
  • Tero必须易于应用。
  • Tero必须易于教学。
  • Tero必须知道如何适应未来。
  • Tero必须极其直观。
  • Tero必须成为开发者的工具。
  • Tero必须是一个框架,通过在尽可能短的时间内制作的解决方案来赚钱。