Nadir PHP 微型框架版本 1

1.0.2 2017-11-23 18:13 UTC

This package is not auto-updated.

Last update: 2024-09-29 02:58:58 UTC


README

又一个 PHP 微型框架。

Latest Stable Version Latest Unstable Version License PHP from Packagist

Nadir 是一个 PHP 微型框架,帮助您快速编写 Web 应用程序、控制台应用程序和 RESTful 服务。它基于 MVC 模式。这个微型框架提供了广泛的修改和定制机会。

  1. 安装
  2. 项目结构
  3. 主要配置文件
  4. 控制器
  5. 视图
  6. 模型
  7. 授权
  8. 数据验证

安装

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(),
            ));
        }
    }
}