selikhovleonid / nadir
Nadir PHP 微型框架版本 1
Requires
- php: >=5.3.0
This package is not auto-updated.
Last update: 2024-09-29 02:58:58 UTC
README
又一个 PHP 微型框架。
Nadir 是一个 PHP 微型框架,帮助您快速编写 Web 应用程序、控制台应用程序和 RESTful 服务。它基于 MVC 模式。这个微型框架提供了广泛的修改和定制机会。
安装
Nadir 至少需要 PHP 5.3 版本。您需要 Composer 依赖管理器来安装此微型框架。开始使用 Nadir 的最简单方法是运行以下 shell 命令创建项目框架
php composer.phar create-project -s dev selikhovleonid/nadir-skeleton <project-name>
项目结构
创建的项目模板将具有类似于以下的结构
├── cli
│ └── cli.php
├── composer.json
├── composer.lock
├── config
│ └── main.php
├── controllers
│ ├── Cli.php
│ ├── System.php
│ └── Test.php
├── extensions
│ └── core
│ ├── AbstractAuth.php
│ ├── AbstractModel.php
│ ├── Auth.php
│ ├── Process.php
│ └── SystemCtrlInterface.php
├── LICENSE
├── models
│ └── Test.php
├── README.md
├── vendor
├── views
│ ├── layouts
│ │ └── main.php
│ ├── snippets
│ │ └── topbar.php
│ └── views
│ ├── system
│ │ ├── page401.php
│ │ ├── page403.php
│ │ └── page404.php
│ └── test
│ └── default.php
└── web
└── index.php
主要配置文件
任何应用程序(控制台或 Web)的配置都包含在 /config/main.php
文件中。这是一个关联数组,当微型框架核心加载时读取。数组可供修改和扩展,默认包括以下元素
array( // The path map of the application components 'componentsRootMap' => array( 'models' => '/models', 'controllers' => '/controllers', 'views' => '/views/views', 'layouts' => '/views/layouts', 'snippets' => '/views/snippets', 'images' => '/web/assets/imgs', 'js' => '/web/js', 'css' => '/web/css' ), // The default name of the layout 'defaultLayout' => 'main', // The routing table that contains the correspondence between the request URN // and the Controller-Action pair 'routeMap' => array( 'cli' => array( '--test' => array( 'ctrl' => array('Cli', 'actionTest'), ), ), 'get' => array( '/' => array( 'ctrl' => array('Test', 'actionDefault'), 'auth' => false, ), '.*' => array( 'ctrl' => array('System', 'actionPage404'), 'auth' => false, ), ), 'post' => array( '.*' => array( 'ctrl' => array('System', 'actionPage404'), 'auth' => false, ), ), 'put' => array( '.*' => array( 'ctrl' => array('System', 'actionPage404'), 'auth' => false, ), ), 'delete' => array( '.*' => array( 'ctrl' => array('System', 'actionPage404'), 'auth' => false, ), ), ), );
通过调用获取客户端代码中的配置
\nadir\core\AppHelper::getInstance()->getConfig('configName')
控制器
控制器是继承自 \nadir\core\AbstractWebController
或 \nadir\core\AbstractCliController
抽象超类的一个实例。当被调用时,控制器执行某些操作,这通常涉及模型以获取数据、它们的进一步转换并将它们传递给视图。
控制器和视图
在 Web 应用程序的生命周期中,查询绑定到控制器-操作对之后,会创建一个控制器对象,该对象默认尝试将视图对象与其关联。
视图通常是复合的,由布局(\nadir\core\Layout
类的实例)和狭义的视图(\nadir\core\View
类的对象)组成。默认视图对象仅在存在关联的标记文件时分配。标记文件的名称是通过从操作名称中删除 'action' 前缀得到的,文件放在以控制器名称命名的目录中(文件名和目录名应小写)。
通过调用访问器 $this->getView()
、$this->setView()
、$this->getLayout()
和 $this->setLayout()
在控制器中可以访问视图对象。在任何页面渲染开始之前,都可以更改默认布局或视图到任何其他可用的单类型对象。
从控制器传递用户变量值到视图
namespace controllers; use nadir\core\AbstractWebCtrl; class Test extends AbstractWebCtrl { public function actionDefault() { // ... $this->getView()->foo = 'foo'; $this->getView()->bar = array(42, 'bar'); $this->getView()->setVariables(array( 'baz' => 'baz', 'qux' => 'qux', 'quux' => 'quux', )); // ... $this->render(); } }
在此视图的 /views/views/test/default.php
标记文件中,变量可以通过调用 $this->foo
、$this->bar
、$this->baz
等进行读取。
通过在操作中调用 $this->render()
来渲染页面。您可以渲染只包含视图文件的页面(在这种情况下,布局必须是 null)。此外,在 AJAX 请求的情况下,通常不需要 HTML 页面的渲染,需要更具体的答案格式,在这种情况下,提供了 \nadir\core\AbstractWebCtrl::renderJson()
方法。
CLI 控制器
shell 命令的示例
php cli.php --test --foo=bar
此命令在查询绑定到路由表后将被 CLI 控制器操作处理
namespace controllers; use nadir\core\AbstractCliCtrl; class Cli extends AbstractCliCtrl { public function actionTest(array $aArgs) { if (!empty($aArgs)) { $this->printInfo('The test cli action was called with args: ' .implode(', ', $aArgs).'.'); } else { $this->printError(new \Exception('The test cli action was called without args.')); } } }
视图
复合视图
视图包含HTML代码和最小逻辑,仅必要的逻辑用于操作从控制器接收到的变量。视图通常是复合的,由布局(\nadir\core\Layout
类的实例)和狭义的视图(\nadir\core\View
类的对象)组成。
视图的每个复合部分可以进一步包含片段(\nadir\core\Snippet
类的对象)——经常遇到的界面元素片段,如导航面板、各种信息块等。
namespace controllers; use nadir\core\AbstractWebCtrl; class Test extends AbstractWebCtrl { public function actionDefault() { // ... $this->setView('test', 'default'); $this->setLayout('main'); $this->getLayout()->isUserOnline = false; $this->getView()->foo = 'foo'; $this->getView()->bar = array(42, 'bar'); // ... $this->render(); } }
在此视图的标记文件/views/views/test/default.php
中,可以通过调用$this->foo
和$this->bar
来读取视图变量。
<!-- ... -->
<div>
<h1><?= $this->foo; ?></h1>
<?php if (is_array($this->bar) && !empty($this->bar)): ?>
<ul>
<?php foreach ($this->bar as $elem): ?>
<li><?= $elem; ?></li>
<?php endforeach; ?>
</ul>
<?php endif; ?>
</div>
<!-- ... -->
同样,在布局的标记文件/views/layouts/main.php
中,可以通过调用$this->isUserOnline
来读取变量。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Nadir Microframework</title>
</head>
<body>
<?php $this->getView()->render(); ?>
</body>
</html>
请注意,视图在布局中的渲染位置由方法调用$this->getView()->render()
的位置决定。
片段
处理片段,通常与处理复合——视图和布局——类似。片段的类也继承了\nadir\core\AbstractView
类,发送和调用用户变量的过程与布局和视图类似。复合可以包含多个片段。片段不能包含另一个片段。
我们将从上一个示例的标记文件中提取部分内容到单独的片段topbar
。文件/views/snippets/topbar.php
将包含以下代码
<h1>User <?= $this->isUserOnline ? 'online' : 'offline'; ?></h1>
控制器动作将如下所示
namespace controllers; use nadir\core\AbstractWebCtrl; class Test extends AbstractWebCtrl { public function actionDefault() { // ... $this->setView('test', 'default'); $this->setLayout('main'); $this->getView()->addSnippet('topbar'); $this->getView() ->getSnippet('topbar') ->isUserOnline = false; $this->getView()->foo = 'foo'; $this->getView()->bar = array(42, 'bar'); // ... $this->render(); } }
在视图中渲染片段topbar
的位置由方法调用$this->getSnippet('topbar')->render()
的位置决定。
<!-- ... -->
<div>
<?php $this->getSnippet('topbar')->render(); ?>
<h1><?= $this->foo; ?></h1>
<?php if (is_array($this->bar) && !empty($this->bar)): ?>
<ul>
<?php foreach ($this->bar as $elem): ?>
<li><?= $elem; ?></li>
<?php endforeach; ?>
</ul>
<?php endif; ?>
</div>
<!-- ... -->
模型
Nadir不包含构建模型的一定规则和规定。MVC模式该组件的具体实现由开发者提供。根据许多因素,模型可以表示为一层(单个对象),也可以表示为多层抽象(关联对象的复杂层次结构)。
namespace models; use extensions\core\AbstractModel; class Test extends AbstractModel { public function readDefault() { // Dummy mode return array( 'foo' => 'bar', 'bar' => array(42, 'baz'), ); } }
namespace controllers; use nadir\core\AbstractWebCtrl; class Test extends AbstractWebCtrl { public function actionDefault() { $this->getView()->addSnippet('topbar'); $this->getView() ->getSnippet('topbar') ->isUserOnline = false; $oModel = new \models\Test(); $aData = $oModel->readDefault(); $this->getView()->foo = $aData['foo']; $this->getView()->bar = $aData['bar']; $this->render(); } }
授权
Nadir为用户授权提供了广泛的定制选项。需要在此处填充\extensions\core\Auth
类以实现具体功能。
namespace extensions\core; use nadir\core\Request; use nadir\core\AppHelper; class Auth extends AbstractAuth { protected $request = null; protected $routeConfig = null; protected $error = null; public function __construct(Request $oRequest) { $this->request = $oRequest; $this->routeConfig = AppHelper::getInstance()->getRouteConfig(); } protected function checkCookies(array $aCookies) { // Put your code here... } public function run() { if (!isset($this->routeConfig['auth'])) { throw new \Exception("Undefined option 'auth' for the current route."); } $mCookies = $this->request->getAllCookies(); $this->checkCookies(!is_null($mCookies) ? $mCookies : array()); } public function isValid() { return is_null($this->error); } public function onFail() { // Put your code here... } }
要实现基于角色的访问控制,还应在主配置文件的路由中添加额外的选项。
'routeMap' => array( // ... 'get' => array( '/' => array( 'ctrl' => array('Test', 'actionDefault'), 'roles' => array('admin', 'manager'), 'auth' => true, ), // ... '.*' => array( 'ctrl' => array('System', 'actionPage404'), 'roles' => array('admin', 'manager', 'user'), 'auth' => true, ), ), // ... ),
数据验证
nadir\core\validator\Validator
类提供了输入数据的验证。其功能可以通过添加新的自定义验证规则来扩展。
namespace controllers; use nadir\core\AbstractWebCtrl; use nadir\core\validator\Validator; class Test extends AbstractWebCtrl { public function actionDefault() { $aData = array( 'foo' => 'fooValue', 'bar' => 'barValue', 'baz' => -42, 'qux' => false, 'quux' => array( 'quuux' => 'quuuxValue', ), ); $oValidator = new Validator($aData); $oValidator->setItems(array( array( array('foo', 'bar'), 'required', ), array( array( 'foo', 'bar', 'quux.quuux' ), 'string', array('notEmpty' => true), ), array( 'bar', 'string', array( 'length' => array('min' => 3, 'max' => 8), 'pattern' => '#^bar#', ) ), array( 'baz', 'number', array( 'integer' => true, 'float' => false, 'positive' => false, 'value' => array('max' => -1), ) ), array( 'qux', 'boolean', array('isTrue' => false), ), array( 'quux', 'array', array( 'assoc' => true, 'length' => array('equal' => 1), ), ), )); if ($oValidator->run()->isValid()) { $this->renderJson(array( 'result' => 'ok', 'errors' => array(), )); } else { $this->renderJson(array( 'result' => 'fail', 'errors' => $oValidator->getErrors(), )); } } }