dromero86 / tero
Tero是一个强大的框架
Requires
- php: >=5.3.0
- composer/installers: ~1.0
This package is not auto-updated.
Last update: 2024-10-02 12:21:24 UTC
README
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工作,我们必须考虑一些事情
- URL模式先于
- GET参数不是按照正则表达式的形式编写的
- GET参数可以使用PHP语法作为可选参数来放置 {$ variable = ""}
- 当我们使用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 程序员朋友带来一个问题,但不要惊慌,我们这样解决。
- 理解 Parser 渲染的强大功能,允许我们连接简单的变量,以后这将允许我们连接我们的数据结果集。
- 理解列表渲染允许我们编写选择性的代码,例如
<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);
稍后,我们可以这样做,只写一行。
因此,好处显而易见;)
- 模板分段,所有可以在块中完成的事情都可以被重用。
- 简化布局,使用简单的变量进行放置和使用
- 易于调试,如果您看到变量 "{sin_renderizar}",则表示出了问题。
- 没有 PHP 错误,如果没有嵌入 PHP 代码,就没有错误的可能性。
- 对 JavaScript 友好,这是区别。
- 易于设计师和布局设计师思考(经证明)
数据集
数据与视图之间的管道。
为了最小化 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 行代码就完成了以下操作
- 填充了一个模板中的数据。
- 如果没有数据,变量将不可见
- 如果您的查询有更多字段,它们将自动渲染为 news_xxxx
- 您可以轻松更改变量的名称,使代码更易读
如果我不想有一个列表,或者我有一个单独的记录要渲染怎么办?
那么,您可以使用“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必须是一个框架,通过在尽可能短的时间内制作的解决方案来赚钱。