超快速 PHP 应用程序开发





5.00/5 (29投票s)
描述了使用免费的 Scavix Web 开发框架 (Scavix-WDF) 进行 PHP 应用程序开发。
引言
本文描述了如何使用免费的 Scavix Web 开发框架 ("Scavix-WDF") 轻松快速地创建可扩展和安全的 Web 应用程序。该框架以 LGPL 发布,任何人都可以免费使用和扩展。它在 GitHub 上提供完整的源代码。
受众
具有扎实的 PHP、JavaScript、Apache 和 SQL 知识的经验丰富的软件开发人员。
历史
在过去几年中,我们为自己的公司开发了多个专业的 Web 应用程序,或作为客户的软件开发项目。在启动新的 Web 应用程序时,主要问题之一是客户总是希望拥有所有源代码的完全权利,尽管重用一些早期开发并已被证明是坚如磐石且快速的组件是合理的。
这就是为什么我们决定创建一个 Web 开发框架并将其以 LGPL 发布,以便任何人(包括我们)都可以使用一些常用的功能。这也使我们为客户开发项目更便宜,维护也更容易、更便宜。在多个项目中使用此类框架的最大优点之一是,错误修复和改进(无论是安全性还是性能方面)都可以直接用于所有基于此框架的其他应用程序中。
结构
该框架可在任何能够执行 PHP 脚本的 Web 服务器上运行。我们更喜欢 Apache Web 服务器,因为它稳定且快速。请确保您在 PHP 设置中启用了 short_open_tag
才能运行该框架。
通常,该框架是应用程序代码和 Web 服务器/数据库之间的底层抽象层。它管理几乎每个 Web 应用程序所需的主要基本功能,包括模板引擎、数据库访问、会话处理、安全性、本地化、翻译、缓存……以及更多功能。
尽管它提供了许多预定义的功能,但它也赋予您在非常低的级别上自行编写代码或 SQL 的全部能力。
那么,让我们深入了解并……
开始使用
WebFramework 旨在帮助您尽快开发 Web 应用程序。因此,最简单的设置是 git clone git://github.com/ScavixSoftware/WebFramework.git 或下载 ZIP。将您的 Web 服务器文档根目录指向 `sample_blog` 文件夹或在其旁边创建您自己的应用程序文件夹。您只需要至少两个文件:`index.php` 和 `config.php`。如果您启用了 `mod_rewrite` 并想要漂亮的 URL(我们知道您想要),也可以添加一个简单的 `.htaccess` 文件。在 sample_blog 应用程序中有一个。本文中的所有 URL 都指的是需要服务器上启用 mod_rewrite 的漂亮 URL。
`index.php` 文件不会包含太多内容,只有几行初始化代码:
<?php
// assuming that WDF sits not directly in your docroot, but next to it
require_once(__DIR__."/../system/system.php");
system_init('sample_blog'); // where 'sample_blog' is the name (folder) of your app
system_execute();
?>
就这样。当 WDF 被包含时,它会自动加载配置文件。还有其他使用它的方法,但我们暂时忽略这些情况。
`config.php` 包含基本的应用程序设置,实际上比索引文件包含的要多。
<?php
// set up default controller
$CONFIG['system']['default_page'] = "Blog";
$CONFIG['system']['default_event'] = "Index";
// Set up the classpath
classpath_add(__DIR__.'/controller');
// Set up the database
$CONFIG['model']['system']['connection_string'] = "sqlite:../blog.db";
// Set up logging
ini_set("error_log", __DIR__.'/../logs/blog_php_error.log');
?>
默认控制器是当用户访问您的域而没有其他参数时将被调用的。我们称之为索引操作。
像大多数其他框架一样,WDF 也提供了一个类自动加载机制。如果您将类存储在名为 `<classname>.class.php` 的文件中,并将其路径添加到 classpath,WDF 将找到它们。听起来很复杂,实际上只是一行代码。
classpath_add(__DIR__.'/controller');
这将使 'controller' 子文件夹中的所有文件都可用于自动加载器。如果这样使用,所有子文件夹也一样。
数据库
我们总是需要一个数据库。这是多年开发的经验法则,因此 WDF 也需要一个才能正常运行。为了让您能够轻松开始,WDF 可以处理 SQLite 数据库(如果您的主机上的 PHP 支持!),因此配置只需一行:
$CONFIG['model']['system']['connection_string'] = "sqlite:../blog.db";
这将使您的数据库以“系统”别名可用。还有许多其他选项,例如连接到 MySQL 或 SQLServer,但我们暂时跳过它们。
日志记录
最后是日志:我们希望它们在 docroot 中或旁边,以便随时轻松访问。因此在这种情况下,我们将 PHP 的错误日志设置为一个新文件夹。WDF 可以处理其他日志格式,例如使用 WdfTracer 工具可读的跟踪格式,但目前我们只使用这种非常简单的方法。
ini_set("error_log", __DIR__.'/../logs/blog_php_error.log');
正如我们在上述配置中指定了“default_page
”一样,我们现在需要设置一个名为“Blog”的控制器,其中包含一个名为“Index”的方法。我们已经调整了 classpath,因此我们创建了一个“controller”文件夹,并在其中创建一个名为 `blog.class.php` 的文件(注意:全部小写!)。
<?php
// blog.class.php
use ScavixWDF\Base\HtmlPage;
class Blog extends HtmlPage
{
function Index()
{
$this->content("Hello world!");
}
}
?>
就这样。现在在浏览器中输入您的 URL `http://your.project.com/Blog/Index`,查看结果。如您所见,此控制器类派生自基类 HtmlPage,其中包含基本的 HTML 结构,可以使用您自己的 CSS 和 JS 进行扩展(请参见下文)。
事实上,您可能还想向方法传递一些参数。WDF 为此提供了一个注解模式。WDF 属性都封装在 @attribute[] 注解中。最常用的是 RequestParam 属性。
您可以这样使用它:
<?php
// blog.class.php
use ScavixWDF\Base\HtmlPage;
class Blog extends HtmlPage
{
function Index(){$this->content("Hello world!");}
/**
* @attribute[RequestParam('index',int,1)]
*/
function GetSome($index)
{
$this->content("You are looking for index $index");
}
}
?>
通过此机制传递的参数会自动根据其类型进行清理,因此无需验证请求为整数类型的用户输入是否存在 <script/> 标签。目前允许的类型有 int、bool、float、string、currency、URI、email、array 和 object。
您可以通过 URL `http://your.project.com/Blog/GetSome?index=123` 或 `http://your.project.com/Blog/GetSome/123` 调用此函数。
为了从代码中构建这些 URL,WDF 提供了 buildQuery 函数,它将遵循您的设置:如果您启用了 `mod_rewrite`,它将构建漂亮的 URL;如果未启用,它将保留 GET 参数语法。
<?php
// blog.class.php
use ScavixWDF\Base\HtmlPage;
class Blog extends HtmlPage
{
function Index()
{
$q = buildQuery('Blog','GetSome',array('index'=>42));
// OR $q = buildQuery('Blog','GetSome','index=42');
$this->content("<a href='$q'>Get Some!</a>");
}
/**
* @attribute[RequestParam('index',int,1)]
*/
function GetSome($index)
{
$this->content("You are looking for index $index");
}
}
?>
到目前为止,功能很酷,但 UI 仍然不是很好看,对吧?那么让我们使用……来美化它。
模板
WDF 遵循 模型-视图-控制器模式,更具体地说,它实现了 表示-抽象-控制模式。
这是理论部分。对我们来说,这意味着每个系统部分都可以作为独立的组件运行(至少是逻辑部分)。一个模板由一个 `.class.php` 文件和它旁边的 `.tpl.php` 文件中的模板组成。
<?php
// templates/ mytemplate.class.php
use ScavixWDF\Base\Template;
class MyTemplate extends Template
{
function __initialize($text)
{
$this->set('text',$text);
}
}
?>
templates/mytemplate.tpl.php:
<div class='hello'><?=$text?></div>
现在有几点需要提及
- 别忘了在 `config.php` 中添加 `classpath_add(__DIR__.'/templates');`
- WDF 不使用外部模板引擎,而是使用 PHP 内联语法
我们决定不使用外部模板引擎,以尽可能降低学习曲线:您已经了解 PHP。这也有助于更轻松地移植现有 PHP 应用程序。
另一个重要原因是大多数模板引擎在模板中放置了太多的逻辑。
当然,您也可以使用内联 PHP 来实现,但我们希望最小化其中的逻辑,因为它根本不具有良好的可读性,并且我们希望将逻辑和表示分离。如果您见过复杂的 smarty 模板,您就知道我们的意思,对吗 :) ?
了解了所有这些,我们的“博客”示例控制器看起来是这样的
<?php
// blog.class.php
use ScavixWDF\Base\HtmlPage;
class Blog extends HtmlPage
{
function Index()
{
$tpl = new MyTemplate('Hello world!');
$this->content( $tpl );
}
}
?>
所以这一切都意味着你必须为每个模板创建两个文件,对吗?
错了。WDF 再次提供了一种快捷方式:匿名模板允许您跳过仅设计组件的逻辑部分。
Template::Make('mytemplate')->set('text','Hello world!');
也可以做到,您可以安全地删除 `mytemplate.class.php` 文件。
WDF 还提供了另一种方式:跳过模板并创建“仅逻辑”组件。这并不意味着它们没有用户界面,而是意味着它们是容器或简单的用户界面控件。这就是为什么这些在 WDF 中被称为“控件”。这是一个代表 `DIV` 元素的示例控件。
<?php
// mycontrol.class.php
use ScavixWDF\Base\Control;
class MyControl extends Control
{
function __initialize($text)
{
parent::__initialize('div');
$this->class = 'hello';
$this->content($text);
}
}
?>
以这种方式使用,上面提到的 `Blog` 类甚至不会改变。但当然,如果控件如此简单,您可以完全跳过它们。
<?php
// blog.class.php
use ScavixWDF\Base\HtmlPage;
use ScavixWDF\Base\Control;
class Blog extends HtmlPage
{
function Index()
{
$div = new Control('div');
$div->class = 'hello';
$div->content('Hello world!');
$this->content($div);
}
}
?>
或使用方法链式调用
<?php
//blog.class.php
use ScavixWDF\Base\HtmlPage;
use ScavixWDF\Base\Control;
class Blog extends HtmlPage
{
function Index()
{
$this->content( new Control('div') )->addClass('hello')->content('Hello world!');
}
}
?>
这和这个是一样的
<?php
//blog.class.php
use ScavixWDF\Base\HtmlPage;
use ScavixWDF\Base\Control;
class Blog extends HtmlPage
{
function Index()
{
Control::Make('div')->appendTo($this)->addClass('hello')->content('Hello world!');
}
}
?>
所以如您所见,实现相同结果有多种方法。这取决于您的偏好和确切的用例。上述每种方法都有其特殊性,例如 `Control::Make()` 方法会欺骗许多 IDE(如 NetBeans),从而拥有 Control 类的智能感知,而 `$this->content()` 方法则缺乏这一点。构造函数方法是常见的方法,但需要最多的代码行,这在功能增多时可能会变得难以阅读。有时甚至组合使用也可能有用。
到目前为止代码不错,但是UI还是很丑?那么让我们来谈谈……
资源
WebFramework 以相同的方式处理客户端静态内容:图像、CSS 和 JavaScript 文件都使用相同的机制进行传递。
虽然 WDF 自身的资源依赖会自动解决,但您需要配置自己的资源文件夹或多个文件夹。对于小型项目,一个“res”文件夹可能就足够了,而大型应用程序可能希望将例如“js”与“css”文件分开,因此 WDF 允许这样做。
这是一个如何设置资源文件夹的示例,通常这只是一种配置。
<?php
$CONFIG['resources'][] = array
(
// list of extensions WDF will search for in this folder
'ext' => 'js|css|png|jpg|jpeg|gif|htc|ico',
// local path to the folder
'path' => realpath(__DIR__.'/res/'),
// URL to the folder, note that this may be an absolute URL too!
'url' => 'res/',
// true to append a virtual 'nocache folder' to the path
'append_nc' => true,
);
?>
如果您想在 HTML 代码中使用其中的资源,您可以像这样简单地获取它的 URL
<?php
$url1 = resFile('myCoolSkin.css');
$url2 = resFile('myCoolSkin/background.png');
?>
这甚至可以在模板中使用,例如 `<img src="<?=resFile('myCoolSkin/background.png')?>" />`。
我们可以再说一次“就这样!”,但这次还有一些其他事情要提。
- 您可以通过在其中一个资源文件夹中创建同名文件来“覆盖”WDF 的资源(创建一个名为 `htmlpage.js` 的文件,看看 WDF 崩溃 :) )。
- WDF 将从您的资源文件夹中获取第一个匹配项。通过这种方式,您可以通过链接资源文件夹和覆盖默认主题来实现某种主题支持。
- 资源文件夹不进行递归搜索,因此在引用资源时,必须相对于资源文件夹进行引用。
- 如果类资源与其名称匹配,则会自动找到它们。
最后一点是最重要的。这意味着在大多数情况下,您根本不需要关心资源。还记得我们上面提到的控制器吗?
我们将其命名为“Blog”,所以如果我们在“`res/`”文件夹中创建文件“`blog.js`”和“`blog.css`”,它们将自动包含。
// res/blog.js
wdf.ready.add(function()
{
$('body').append("blog.js loaded");
});
/* res/blog.css */
body { color: red; }
这种基于名称的资源加载也能够加载基类的资源。简单的句子,但功能强大!
这意味着如果您为所有控制器类实现了一个基类,那么该基类可以拥有资源,并且这些资源将自动包含在当前类的资源之前。因此,您可以安全地覆盖派生类中的 CSS 类或 JS 对象。
如果你想反过来(在父级资源之前包含我的资源),你可以通过在你的类中添加资源属性来做到这一点:
<?php
/**
* @attribute[Resource('mysubcontroller.js')]
* @attribute[Resource('mysubcontroller.css')]
* @attribute[Resource('other_stuff.js')]
*/
class MySubcontroller extends MyControllerBase
{
/* add some very important and useful code ... */
}
?>
这将按此顺序加载 `mysubcontroller.js`、`mysubcontroller.css`、`mycontrollerbase.js`、`mycontrollerbase.css`,当然,仅当它们存在时。
当然,您也可以使用资源属性加载任何您想要的内容,例如上面示例中的 `other_stuff.js` 文件。
关于缓存的注意事项:整个资源搜索过程在 CPU 周期和网络流量方面都相当昂贵(想象一下具有中央文件存储的集群设置等)。因此 WDF 将在内部缓存资源搜索的结果。
如果您使用某种全局缓存机制,WDF 会将结果存储在那里,这样其他用户就不必再次执行这些递归搜索。至少,如果您没有在 WDF 应用程序中配置任何全局缓存机制(例如 MemCache 或 APC,见下文),它会将结果存储在用户的 SESSION 中。因此,如果您添加了之前引用的资源文件,则必须刷新您的会话。
听起来很复杂,但只需删除您的 cookie 就可以了(除非您使用全局缓存!在这种情况下,您也必须清除该缓存)。
在我们的示例中,当您在浏览器中重新加载页面时,两个资源文件 `blog.js` 和 `blog.css` 不会被加载,因为您在这些文件存在之前加载了页面,并且 WDF 已缓存了此信息。
对 JS 代码感到好奇吗?让我们转到……
客户端
每个现代 Web 应用程序都包含许多客户端部分,换句话说,一个漂亮的 UI 需要您也编写 JS 代码。WDF 封装了一些常见用例,并大量使用了 jQuery 和 jQuery UI。AJAX 通信会自动封装,因此控制器可以返回不同类型(对象、字符串等),而 JS 端仍然可以在没有解析错误的情况下使用它们。这要求您使用 `wdf.post()` 和 `wdf.get()` JS 函数而不是相应的 jQuery 方法,但我们认为这并不算太糟。
全局 `wdf` JS 对象提供了一些有用的快捷方式,例如封装当前活动页面的 post 和 get 信息的 `wdf.controller` 对象,或者确保在调用 `location.reload()` 时省略 GET 参数的 `wdf.reloadWithoutArgs()` 函数。
您可能还会发现 AjaxResponse 和 AjaxAction 类有助于在 PHP 中生成 JS 代码。
连接数据库
如上所述:您将需要一个数据库连接。WDF 提供了几种不同的方法来从 MySQL 或 SQLite 数据库获取数据。
首先,您需要一个 DataSource 对象。根据上面的示例配置,我们配置了一个别名为“system”的数据源,因此 `model_datasource()` 函数会将其返回给您。
假设数据库中有一个名为“blog”的表,其中包含“id”(整数)、“title”(varchar)和“body”(text)列。
此示例展示了使用 WDF 查询数据行的不同方法,它们会产生相同的结果。
<?php
$ds = model_datasource('system');
foreach( $ds->ExecuteSql("SELECT * FROM blog WHERE id>10 ORDER BY id DESC") as $row )
echo "{$row['id']} - {$row['title']}";
foreach( $ds->ExecuteSql("SELECT * FROM blog WHERE id>? ORDER BY id DESC",10) as $row )
echo "{$row['id']} - {$row['title']}";
foreach( $ds->Query('blog')->gt('id',10)->orderBy('id','DESC') as $row )
echo "{$row->id} - {$row->title}";
//You may also create a model for the table and use it like this:
class BlogPost extends Model{ function GetTableName(){ return 'blog'; } }
foreach( BlogPost::Make()->gt('id',10)->orderBy('id','DESC') as $row )
echo "{$row->id} - {$row->title}";
?>
将这些内容放入我们的博客应用程序示例上下文中,结合我们现在所了解的所有知识,将如下所示:
<?php
// blog.class.php
use ScavixWDF\Base\HtmlPage;
use ScavixWDF\Base\Control;
class Blog extends HtmlPage
{
function Index()
{
foreach( BlogPost::Make()->limit(20)->orderBy('id','DESC') as $post )
{
$q = buildQuery('Blog','GetSome',$post->id);
Control::Make('div')->appendTo($this)->content("<a href='$q'>{$post->title}</a><br/>");
}
}
/**
* @attribute[RequestParam('index',int,1)]
*/
function GetSome($index)
{
$post = BlogPost::Make()->eq('id',$index)->current();
Control::Make('div')->appendTo($this)->content("<h1>{$post->title}</h1><p>{$post->text}</p>");
}
}
?>
您会再次看到代码中的一些方法链式调用。有相当多的方法可供使用,请参阅API 参考中的 Model 类以获取所有方法。
一些是
- equal, eq: 测试是否相等
- lowerThan, lt: 测试是否小于
- greaterThan, gt: 测试是否大于
- isNull: 测试是否为 NULL
- like: 测试是否相似(带 % 占位符)
- andX: 嵌套一个 AND 查询
- orAll: 将所有后续查询视为通过 OR 连接
- ....
您可以随时在 `foreach` 循环中使用查询,因为它像数组一样可访问(参见上面的示例)。
您也可以对不同的查询使用相同的基本查询。
<?php
$q = BlogPost::Make()->gt('id',10)->lt('id',100);
$q_asc = $q->orderBy('id','ASC');
$q_desc = $q->orderBy('id','DESC');
?>
当您通过迭代或以下方法之一访问数据时,查询将被执行(它们会破坏链式调用!):
current()
: 以 Model 对象返回当前行scalar($field)
: 返回当前行中的一列enumerate($field,$distinct)
: 返回 $field 的所有值(如果 $distinct 为 true,结果将不包含任何重复项)
调试代码
WDF 提供了比正常的 PHP `error_log` 函数更多的日志记录和调试功能。我们最常用的是 `log_debug()`,它会将传递的数据写入您配置的任何位置。还有其他有用的函数,如 `log_error()`、`log_warn()`、`log_hint()`,它们写入相同的日志,但具有不同的严重性,因此可以轻松过滤日志。此外,您可以为日志条目添加类别(例如用户名),以包含更多信息。
如何使用呢?非常简单!首先在 `config.php` 中进行配置:
<?php
$CONFIG['system']['logging']['human_readable'] = array
(
'path' => __DIR__.'/../logs/',
'filename_pattern' => 'blog_wdf.log',
);
?>
这将创建一个记录器,它将写入“logs”文件夹中的“`blog_wdf.log`”文件。还有更多设置,如日志轮转和生命周期,但我们暂时将其保留为默认值。有关其他选项,请参阅 WDF 日志记录文档。结果将类似于正常的 `error_log` 输出,所以让我们看看真正强大的跟踪记录器。
<?php
$CONFIG['system']['logging']['trace'] = array
(
'class' => 'TraceLogger',
'path' => __DIR__.'/../logs/',
'filename_pattern' => 'blog_wdf.trace',
);
?>
这将把日志条目放入“`blog_wdf.trace`”文件中。使用 WebFramework 仓库中的 WdfTracer 应用程序来读取这些文件:它们提供了带有完整参数内容的堆栈跟踪,WdfTracer 允许您双击在 IDE 中打开文件。这确实节省时间!
顺便说一句:您可以同时配置两个(甚至更多)日志记录器,以便写入不同的日志。我们经常在实时系统中使用可读日志记录器,并在开发和测试服务器上使用跟踪器来捕获错误。
一旦配置好,记录就很容易了。
<?php
// blog.class.php
use ScavixWDF\Base\HtmlPage;
use ScavixWDF\Base\Control;
class Blog extends HtmlPage
{
function Index()
{
log_info("Index called");
foreach( BlogPost::Make()->limit(20)->orderBy('id','DESC') as $post )
{
log_debug("BlogPost:",$post);
$q = buildQuery('Blog','GetSome',$post->id);
log_debug("URL:",$q);
Control::Make('div')->appendTo($this)->content("<a href='$q'>{$post->title}</a><br/>");
}
}
/**
* @attribute[RequestParam('index',int,1)]
*/
function GetSome($index)
{
log_info("GetSome called",$index);
$post = BlogPost::Make()->eq('id',$index)->current();
log_debug("BlogPost:",$post);
Control::Make('div')->appendTo($this)->content("<h1>{$post->title}</h1><p>{$post->text}</p>");
}
}
?>
这将用一些数据填充你的日志/追踪文件。
其中你会看到这样的条目:“未找到翻译!”
那么,让我们看看这意味着什么……
翻译
我们从多年的开发经验中吸取了另一个教训:你总是需要一个好的翻译机制!
因此,WDF 将要求您创建一个,但也会使其尽可能简单。您可以随时通过使用“TXT_MAIN”或“BTN_SAVE”等文本占位符而不是实际文本来创建翻译。WDF 将识别它们并为您准备好一切,以便使用其集成的 SysAdmin 控制器创建翻译(见下文)。
WDF 将识别以 'TXT_'、'BTN_'、'ERR_'、'WINDOW_' 和 'TITLE_' 开头的占位符(请注意,这是区分大小写的,所以请大写书写)。
为了使翻译非常简单,WDF 集成了一个简单的翻译系统。实际上,您也可以使用专业的翻译系统,但目前已实现了 POEditor.com。现在我们将展示集成翻译器。
<?php
// where to store fetched translations
$CONFIG['translation']['data_path'] = realpath(__DIR__.'/strings');
// which datasource to use for storing information about missing strings
$CONFIG['translation']['sync']['datasource'] = 'system';
// translation service (Scavix, PoEditor)
$CONFIG['translation']['sync']['provider'] = 'Scavix';
?>
所有文本都存储在 Web 服务器上的 `.php` 文件中,以便在 `data_path` 文件夹中快速访问和轻松管理。当您使用 SysAdmin 的“获取字符串”机制时,它会从在线翻译平台(即 POEditor.com)获取所有文本,并将这些文本存储在 `data_path` 文件夹中单独的语言包含文件中,每种语言一个文件。这让您可以完美地控制仅在您之前在开发或测试系统上检查过文本之后才更新实时系统上的文本(在 POEditor 中编辑 -> 获取到开发系统 -> 检查 -> 上传到实时服务器)。当您将这些文件以及应用程序的所有其他源代码一起存储在源代码仓库(如 Git 或 SVN)中时,它还可以完美地跟踪所有文本的所有更改。
目前为止都是理论,这里有一些示例:
<?php
use ScavixWDF\Base\HtmlPage;
class Blog extends HtmlPage
{
function Index()
{
$this->title = "WINDOW_MYBLOG";
$this->content("<h1>TITLE_MYBLOG</h1>");
$this->content("<p>TXT_MYBLOG_INTRO</p>");
//...
}
}
?>
<div class='blogpost'>
<div>TXT_POST_HEADER</div>
<div><?=$title?></div>
<div><?=$text?></div>
<div>TXT_POST_FOOTER</div>
</div>
看到了吗?一旦配置好,你只需在任何地方使用文本常量。WDF 会通知你需要为这些未定义常量添加一些文本。
这是通过集成……来实现的。
SysAdmin
它的诞生源于需要在开发过程中维护系统的某些部分。翻译是开发应用程序时最常更改的东西,但还有更多,例如清除缓存等等。
所以我们把它打包到一个控制器中,为了安全起见,默认是禁用的。
我们强烈建议您仅在开发系统上启用它!
<?php
$CONFIG['system']['admin']['enabled'] = true;
$CONFIG['system']['admin']['username'] = 'admin';
$CONFIG['system']['admin']['password'] = 'admin';
?>
您现在可以通过浏览器访问 SysAdmin,例如:http://your.project.dev.url/SysAdmin/,并使用您配置的凭据登录(在我们的示例中为 admin:admin)。
这就是创建新字符串时的样子
字符串创建后,您可能需要翻译它们。以下截图显示了集成的翻译器,但如果您选择 PoEditor,则必须在那里进行翻译。
这是从翻译系统获取字符串到项目时的截图。
请注意,翻译甚至新字符串的创建对您的项目都不会有任何影响,直到您“获取”它们。通过这种方式,我们确保一些人可以编辑字符串,而另一些人可以在代码上工作,而不会一直被新文本所困扰。
最后,您可能已经有一些翻译或者您更喜欢其他翻译系统,因此您可以使用导入功能将 JSON 编码字符串上传到您的项目中。
在 GitHub 的 API 参考文档 中有一些关于格式的提示。
其他 SysAdmin 模块包括:
- Minify(CSS 和 JS 代码)
- 缓存(信息和管理)
- PhpInfo(可搜索以轻松查找错误/配置)
模块
WDF 提供了一些额外的功能,但为了保持较小的占用空间,这些功能作为可选模块提供给 Web 应用程序使用。猜猜看:您会在 'modules' 文件夹中找到它们 :) 。
加载模块有两种方法
- 将其添加到 $CONFIG['system']['modules'] 数组中
- 使用 system_load_module 函数
有各种模块,其中一些是:
- 浏览器:来自 UserAgent 的浏览器检测
- geoip:GeoIP 服务
- mail:通过 SMTP 发送邮件
- textdata:CSV、LDIF 和 VCard 处理
- payment:原始支付类/接口
- globalcache:超越 SESSION 边界的缓存
- minify:压缩 JS 和 CSS
它们都非常有用,但我们只会描述最后两个用于……
优化速度
WDF 的设计宗旨是开箱即用,但有一些技术可以进一步加快您的应用程序速度。WDF 将在至少其中两方面为您提供帮助:缓存和压缩 JS 和 CSS 代码。
缓存是自动进行的,某些部分(如上面提到的资源搜索)会自动缓存,但您当然可以使用相同的函数来缓存您自己的数据。这些函数将在两个级别工作:会话缓存和全局缓存。
会话缓存基本上是您所期望的:它使用 SESSION 变量来设置/获取数据。全局缓存用于缓存所有会话的数据,因此它是独立于用户的。这确实可以大大提升您的应用程序,因为只有第一个用户会执行操作,然后(在指定的时间内)该结果也将用于所有其他用户。更重要的是:WDF 提供了自动缓存数据库查询的方法,这样您就不需要在执行之前检查缓存。
<?php
$currencies = model_datasource('system')->CacheExecuteSql(
"SELECT * FROM currencies",false,3600); // cached for 1 hour
cache_set('my_data',array('hello'=>'world')); // stores my_data forever
cache_set('my_data',array('hello'=>'world'),300); // stores my_data for 5 minutes
cache_set('my_data',array('hello'=>'world'),300,false,true);
// stores my_data for 5 minutes but only in the SESSION (for this user)
$md = cache_get('my_data',array());
// returns my_data from the cache or an empty array as default if it's not present in the cache
?>
所以我们使用了全局缓存,那是什么呢?
WDF 可以使用不同的缓存:eAccelerator (1)、MemCache (2)、Zend (3)、APC (4) 或数据库 (5)。'Database' 是我们自己的实现,可以与任何定义的数据源一起使用。
要使用它,我们只需要几行配置。
<?php
$CONFIG['system']['modules'][] = 'globalcache'; // load the globalcache module
$CONFIG['globalcache']['CACHE'] = 5; // DB cache is used
$CONFIG['globalcache']['datasource'] = 'system'; // alias of datasource to use
?>
当然,其他缓存需要其他配置,但我们暂时跳过。
现在,还有第二种方法可以几乎不费力地加速您的应用程序:压缩 JS 和 CSS 代码。
这也由一个名为“minify”的 WDF 模块完成。它会找出您的应用程序中使用的资源,收集它们并压缩它们以使其变小。当然,您知道压缩的好处,对吗?所以我们跳过解释压缩 JS 和 CSS 代码的好处。
只需几行配置,您的应用程序即可为此做好准备:
<?php
// load the minify module
$CONFIG['system']['modules'][] = 'minify';
// where to store the minified files in the filesystem
$CONFIG['minify']['target_path'] = __DIR__.'/res';
// root url of the res folder
$CONFIG['minify']['url'] = cfg_get('system','url_root').'res/';
// a base for their filename
$CONFIG['minify']['base_name'] = "sample_blog";
?>
现在您可以使用 SyAdmin 的“压缩”菜单在“`target_path`”文件夹中创建压缩文件。
注意:一旦它们被创建,除非您从文件系统中删除它们,否则它们将被使用,所以在开发中这可能不是一个好主意。
关于作者
Scavix Software 是一家德国软件公司,专注于为中大型公司提供软件开发。在过去几年中,我们为 Intel、AMD、Sage、eBay 和 Skype 等公司开发了许多应用程序(主要是 Web 应用程序)。无论您的公司需要开发什么软件,请联系我们获取报价。我们的座右铭是“德国高质量软件工程”,这也是您可以从我们这里得到的。本文由 Daniel Spors、Steffen Macke 和 Christoph Bünger 撰写,他们都就职于 Scavix Software。
更新日志
- 2013/03/09: 初次发布
- 2013/03/28: 一些改进。将 PHPTracer 更名为 WdfTracer。
- 2013/04/25: 更新了关于翻译的部分,以反映 WebFramework 中的新功能
- 2013/04/26: 更新了一些日志记录的路径和文件名,以匹配 WebFramework 中的更改
- 2013/05/08: 更改了一些文件名,从 'sample*' 改为 'blog*',因为我们现在有另一个示例
- 2013/06/12: 添加了 DaSpors 作为作者
- 2014/10/31: 添加了命名空间代码