1. 认识 Phalcon
Phalcon是一个C拓展方式的PHP框架,安装拓展成功即可使用,性能很高。
Phalcon是一个高度解耦的框架。就像给了你一堆积木,你是拼成变形金刚呢还是芭比娃娃呢,随意打造。但是也提高了门槛,小白面对空文件夹不知道如何上手,因此官方有了phalcon-devtools工具帮你快速搭建框架;也有了包含了许多注册好的服务默认的工厂类FactoryDefault。
这是一个提供了 MVC 模式的框架,上手需要了解一下Phalcon中使用模型,使用控制器,和使用视图的一些操作。
Phalcon算是比较现代化的框架了,所以在使用之前先把命名空间,DI等等了解了。
全栈框架,该有的都有了;但是不顺手的地方也可以自己打造,非常自由,简直放飞自我。
本文的目的就是对Phalcon的不同项目中的项目结构捋一遍,从而对Phalcon框架的框架架构有一个初步的认识;然而在实际开发中,我们的代码架构会更加复杂一些。
2. 开始入手
2.1 安装
- 先安装Phalcon;
- 然后安装phalcon-devtools;我们通过phalcon-devtools构建一些项目demo来了解这个框架。刚上手时,phalcon-devtools这个工具特别有用。
2.2 使用 phalcon-devtools
学会使用phalcon-devtools在一开始学习的时候特别有用。
在自动化创建一个项目时,phalcon-devtools工具提供了 cli, micro, simple, modules 四种模式的项目结构。将这四个看一遍就可以对整体架构有一定的认识。
2.3 关于MVC架构
Github上有官方提供的MVC架构的例子:https://github.com/phalcon/mvc
3. 微应用(micro类型项目)
3.1 工具创建一个微应用
命令行中执行以下语句创建一个micro类型的项目:
phalcon create-project Phalcon-micro micro
可以看到大概是这么个目录:
Phalcon-micro/
.phalcon/
app/
config/
migrations/
models/
views/
app.php
public/
css/
files/
img/
js/
temp/
.htaccess
index.php
.htaccess
index.html
微框架应用只需要书写极少的代码即可创建一个PHP应用,适用于书写小的应用, API或原型等。
3.2 入口文件index.php
public/index.php文件内容解析:
<?php
use Phalcon\Di\FactoryDefault; // 工厂默认DI
use Phalcon\Mvc\Micro; // 引入微应用
error_reporting(E_ALL); // 设置报错级别
define('BASE_PATH', dirname(__DIR__)); // 定义根目录
define('APP_PATH', BASE_PATH . '/app'); // 定义app目录
try {
/**
* FactoryDefault已经自动注册了提供全栈框架功能的服务。这些默认服务可以根据自己的习惯重写。
*/
$di = new FactoryDefault(); // 实例化FactoryDefault
/**
* 包含service文件,即是用来注册服务的文件
*/
include APP_PATH . '/config/services.php'; // 引入app/config/service.php
/**
* 容器中获取配置服务
*/
$config = $di->getConfig();
/**
* 包含自动加载文件
*/
include APP_PATH . '/config/loader.php'; // 引入app/config/loader.php
/**
* 实例化微应用,并传入容器示例
*/
$app = new Micro($di);
/**
* 包含应用引导文件
*/
include APP_PATH . '/app.php'; // 引入app/app.php
/**
* 执行控制请求
*/
$app->handle();
} catch (\Exception $e) {
echo $e->getMessage() . '<br>';
echo '<pre>' . $e->getTraceAsString() . '</pre>';
}
接下来根据入口文件的流程,来走一遍,config.php文件就不说了
3.3 服务注册文件service.php
<?php
use Phalcon\Mvc\View\Simple as View;
use Phalcon\Mvc\Url as UrlResolver;
/**
* 单例模式(共享)的配置服务
*/
$di->setShared('config', function () {
return include APP_PATH . "/config/config.php";
});
/**
* 设置视图组件(全局共享)
*/
$di->setShared('view', function () {
$config = $this->getConfig(); // 获取配置
$view = new View(); // 实例化视图
$view->setViewsDir($config->application->viewsDir); // 设置视图路径(从配置文件config.php中获取)
return $view; // 返回实例
});
/**
* URL组件,用来生成应用所有类型的url
*/
$di->setShared('url', function () {
$config = $this->getConfig(); // 获取配置
$url = new UrlResolver(); // 实例化url类
$url->setBaseUri($config->application->baseUri); // 设置基URI(从配置文件config.php中获取)
return $url; // 返回url类示例
});
/**
* 数据库连接,根据配置文件中的设置
*/
$di->setShared('db', function () {
$config = $this->getConfig(); // 获取配置
$class = 'Phalcon\Db\Adapter\Pdo\\' . $config->database->adapter; // 数据库类型(Mysql),根据类型定位到对应的数据库操作PDO类
$params = [
'host' => $config->database->host,
'username' => $config->database->username,
'password' => $config->database->password,
'dbname' => $config->database->dbname,
'charset' => $config->database->charset
]; // 读取其它配置
/* 如果数据库是Postgresql,注销掉charset参数 */
if ($config->database->adapter == 'Postgresql') {
unset($params['charset']);
}
$connection = new $class($params); // 实例化数据库实例
return $connection; // 返回数据库实例
});
3.4 自动加载配置文件loader.php
<?php
/**
* 注册自动加载
*/
$loader = new \Phalcon\Loader();
/* 这里自动加载了modelsDir路径(配置) */
$loader->registerDirs(
[
$config->application->modelsDir
]
)->register();
3.5 应用引导文件app.php
<?php
/**
* Local variables 本地变量
* @var \Phalcon\Mvc\Micro $app 微应用实例
*/
/**
* 在这里添加路由
* 根目录默认index
*/
$app->get('/', function () {
echo $this['view']->render('index');
});
/**
* 找不到时的操作(返回404)
*/
$app->notFound(function () use($app) {
$app->response->setStatusCode(404, "Not Found")->sendHeaders(); // 设置状态码404
echo $app['view']->render('404'); // 转到404页面
});
这样基本上这个微应用,Phalcon最简单的一种实现就跑起来了。
3.6 小结
一个内置的微应用,可以快速构建简单的应用。
4. 简单的单模块应用(simple类型项目)
4.1 大概的项目目录
Phalcon-simple/
app/
config/
config.php
loader.php
router.php
services.php
controllers/
ControllerBase.php
IndexController.php
library/
migrations/
models/
views/
index/
idnex.volt
index.volt
cache/
public/
css/
files/
img/
js/
temp/
.htaccess
index.php
.htaccess
.htrouter.php
index.html
4.2 index.php
同样从index.php入口文件入手:
<?php
use Phalcon\Di\FactoryDefault; // 工厂默认DI
error_reporting(E_ALL);
define('BASE_PATH', dirname(__DIR__)); // 定义入口根目录路径
define('APP_PATH', BASE_PATH . '/app'); // 定义app目录路径
try {
/**
* 实例化默认工厂DI,已经自动注册了很多提供全栈框架的服务
*/
$di = new FactoryDefault();
/**
* 引入router路由文件
*/
include APP_PATH . '/config/router.php';
/**
* 引入services服务注册文件
*/
include APP_PATH . '/config/services.php';
/**
* 为下面的内置设置,获取配置服务
*/
$config = $di->getConfig();
/**
* 包含自动加载loader.php文件
*/
include APP_PATH . '/config/loader.php';
/**
* 加载请求控制
*/
$application = new \Phalcon\Mvc\Application($di);
echo str_replace(["\n","\r","\t"], '', $application->handle()->getContent()); // 移除换行,回车,跳格
} catch (\Exception $e) {
echo $e->getMessage() . '<br>';
echo '<pre>' . $e->getTraceAsString() . '</pre>';
}
4.3 router.php路由文件
获取路由服务,定义路由,执行。
<?php
$router = $di->getRouter();
// Define your routes here
// 在这里定义路由
$router->handle();
4.4 services.php服务注册文件
<?php
use Phalcon\Mvc\View; // view层
use Phalcon\Mvc\View\Engine\Php as PhpEngine; // view层php引擎
use Phalcon\Mvc\Url as UrlResolver; // url组件
use Phalcon\Mvc\View\Engine\Volt as VoltEngine; // volt引擎
use Phalcon\Mvc\Model\Metadata\Memory as MetaDataAdapter; // 元数据适配器
use Phalcon\Session\Adapter\Files as SessionAdapter; // session文件适配器
use Phalcon\Flash\Direct as Flash; // 闪存服务
/**
* 共享的配置服务
*/
$di->setShared('config', function () {
return include APP_PATH . "/config/config.php"; // 配置文件所在路径
});
/**
* URL组件,用来生成所有类型的应用url
*/
$di->setShared('url', function () {
$config = $this->getConfig(); // 获取配置
$url = new UrlResolver(); // 实例化
$url->setBaseUri($config->application->baseUri); // 传入基URI
return $url; // 返回URL实例
});
/**
* 设置视图组件
*/
$di->setShared('view', function () {
$config = $this->getConfig(); // 获取配置
$view = new View(); // 实例化
$view->setDI($this); // 设置为服务
$view->setViewsDir($config->application->viewsDir); // 传入views路径
// 注册模板引擎; 注册了.volt和.phtml结尾的文件
$view->registerEngines([
'.volt' => function ($view) {
$config = $this->getConfig(); // 获取配置
$volt = new VoltEngine($view, $this); // 实例化引擎
$volt->setOptions([
'compiledPath' => $config->application->cacheDir,
'compiledSeparator' => '_'
]); // 设置参数;缓存路径和缓存文件连接符
return $volt; // 返回实例
},
'.phtml' => PhpEngine::class
]);
return $view; // 返回实例
});
/**
* 数据库连接
*/
$di->setShared('db', function () {
$config = $this->getConfig(); // 获取配置
$class = 'Phalcon\Db\Adapter\Pdo\\' . $config->database->adapter; //数据库类型(mysql)
$params = [
'host' => $config->database->host,
'username' => $config->database->username,
'password' => $config->database->password,
'dbname' => $config->database->dbname,
'charset' => $config->database->charset
];
/* 如果是Postgresql数据库,删除charset参数 */
if ($config->database->adapter == 'Postgresql') {
unset($params['charset']);
}
$connection = new $class($params); //实例化
return $connection; //返回连接实例
});
/**
* 如果配置了元数据适配器就使用原数据适配器否则使用内存
*/
$di->setShared('modelsMetadata', function () {
return new MetaDataAdapter();
});
/**
* 注册Bootstrap框架类中的会话闪存服务
*/
$di->set('flash', function () {
return new Flash([
'error' => 'alert alert-danger',
'success' => 'alert alert-success',
'notice' => 'alert alert-info',
'warning' => 'alert alert-warning'
]);
});
/**
* 启动session,当有组件请求session服务的时候
*/
$di->setShared('session', function () {
$session = new SessionAdapter(); // session适配器
$session->start();
return $session;
});
可以看到simple项目已经使用了view层,引入了volt引擎,session服务等,甚至还注册前端bootstrap框架的class。
4.5 loader.php自动加载文件
<?php
$loader = new \Phalcon\Loader();
/**
* 自动加载配置文件中定义好的路径
*/
$loader->registerDirs(
[
$config->application->controllersDir,
$config->application->modelsDir
]
)->register();
4.6 controllers
controllers下定义了一个ControllerBase类,和一个IndexController类:
ControllerBase.php
<?php
use Phalcon\Mvc\Controller;
class ControllerBase extends Controller
{
}
IndexController.php
<?php
class IndexController extends ControllerBase
{
public function indexAction()
{
}
}
基本上simple这个项目就是一个简单的省略了modle的基本单模块项目。
4.7 小结
一个简单的单模块web应用。
5. cli命令行应用(cli类型项目)
5.1 大概的项目目录
Phalcon-cli/
app/
config/
config.php
loader.php
services.php
models/
tasks/
MainTask.php
VersionTask.php
bootstrap.php
public/
css/
js/
run
cli项目的架构看上去更加简单明了。
5.2 run文件
run文件是用来执行项目的入口文件,只需要在命令行执行:
php run
输出:
Congratulations! You are now flying with Phalcon CLI!
这是在MainTask.php中输出的内容。
看一下run文件,就是执行了一段php代码,而这段代码只是引入的bootstrap引导文件:
#!/usr/bin/env php
<?php
include __DIR__ . "/app/bootstrap.php";
5.3 bootstrap.php引导程序
<?php
use Phalcon\Di\FactoryDefault\Cli as CliDi; //引入默认工厂DI的cli模式
use Phalcon\Cli\Console as ConsoleApp; // 引入用来创建cli应用的Console组件
define('BASE_PATH', dirname(__DIR__));
define('APP_PATH', BASE_PATH . '/app');
/**
* 实例化默认工厂DI的cli模式,自动加载提供了全栈框架的服务
*/
$di = new CliDi();
/**
* 包含服务注册文件
*/
include APP_PATH . '/config/services.php';
/**
* 获取配置服务中的配置
*/
$config = $di->getConfig();
/**
* 包含自动加载
*/
include APP_PATH . '/config/loader.php';
/**
* 创建一个console应用(cli)
*/
$console = new ConsoleApp($di);
/**
* 处理console参数
*/
$arguments = [];
foreach ($argv as $k => $arg) {
if ($k == 1) {
$arguments['task'] = $arg; // 第一个参数task类
} elseif ($k == 2) {
$arguments['action'] = $arg; // 第二个参数action方法
} elseif ($k >= 3) {
$arguments['params'][] = $arg; // 第三个参数,额外参数(数组)
}
}
try {
/**
* Handle传入处理好的参数
*/
$console->handle($arguments);
/**
* 如果配置中printNewLine设置了true,将会在末尾另起一行
*
* 仅仅只是为了在命令行中更直观的表现
*/
if (isset($config["printNewLine"]) && $config["printNewLine"]) {
echo PHP_EOL; // 输出一个换行符
}
} catch (Exception $e) {
echo $e->getMessage() . PHP_EOL;
echo $e->getTraceAsString() . PHP_EOL;
exit(255);
}
5.4 service.php服务注册
注册了配置服务和数据库服务:
<?php
/**
* 注册共享配置服务
*/
$di->setShared('config', function () {
return include APP_PATH . '/config/config.php'; // 配置文件路径
});
/**
* 数据库连接
*/
$di->setShared('db', function () {
$config = $this->getConfig(); // 获取配置
$class = 'Phalcon\Db\Adapter\Pdo\\' . $config->database->adapter; // 数据库类型(Mysql)
$params = [
'host' => $config->database->host,
'username' => $config->database->username,
'password' => $config->database->password,
'dbname' => $config->database->dbname,
'charset' => $config->database->charset
];
/* 如果数据库是Postgresql,删除charset变量 */
if ($config->database->adapter == 'Postgresql') {
unset($params['charset']);
}
$connection = new $class($params); // 数据库实例
return $connection; // 返回数据库实例
});
5.5 loader.php自动加载
cli项目与普通web项目不一样的是,自动加载了tasks目录和models目录,但实际上道理是一样的,task任务就好比是Controller。
<?php
$loader = new \Phalcon\Loader();
$loader->registerDirs([
APP_PATH . '/tasks',
APP_PATH . '/models'
]);
$loader->register();
5.6 tasks目录
看一下这个demo项目的两个task文件。
MainTask.php:
<?php
class MainTask extends \Phalcon\Cli\Task
{
public function mainAction()
{
echo "Congratulations! You are now flying with Phalcon CLI!";
}
}
VersionTask.php:
<?php
class VersionTask extends \Phalcon\Cli\Task
{
public function mainAction()
{
$config = $this->getDI()->get('config'); //获取配置
echo $config['version']; //输出版本
}
}
都继承了 Phalcon\Cli\Task 类;MainTask 为系统默认类,类似于默认IndexController那么个意思。
5.7 小结
一个cli模式的demo,可以用来编写简单的cli脚本。
6. 多模块项目(modules类型项目)
6.1 项目目录
这个多模块项目就文件稍微多一点了,可以看到这里有两个模块,一个是web项目,一个是cli项目。
Phalcon-modules
app/
common/
library/
models/
config/
config.php
loader.php
routes.php
services.php
services_cli.php
services_web.php
modules/
cli/
migrations/
tasks/
MainTask.php
VersionTask.php
Module.php
frontend/
controllers/
ControllerBase.php
IndexController.php
models/
views/
index/
index.volt
index.volt
Module.php
bootstrap_cli.php
bootstrap_web.php
cache/
volt/
public/
css/
files/
img/
js/
temp/
.htaccess
index.php
.htaccess
.htrouter.php
index.html
run
大概地看一下文件,可以发现这个模块是simple项目和cli项目合并起来的一个项目,所以之前看过的内容我们就不重复关注了,我们还是重点放在整体架构分析和不同的地方。
6.2 index.php入口文件
依然是入口文件,不过这次这个入口文件变了:
<?php
require '../app/bootstrap_web.php';
只有这么一句,因为public入口下指引的应该是web项目,所以这里直接引入了web项目的指引程序文件bootstrapweb.php。
基本相当于将各个模块的入口文件放到了app目录下,以bootstrap开头命名:
common/
config/
modules/
cli/
migrations/
tasks/
Module.php
frontend/
controllers/
models/
views/
Module.php
bootstrap_cli.php
bootstrap_web.php
common为公共的模块,公共类公共模型什么的放这里;config配置目录,与之前的区别是,services.php放公共服务文件,services_cli.php放cli模块的服务文件,services_web.php放web项模块的服务文件。modules则为两个模块的具体文件,区别是底下多了一个Module.php。
6.3 config.php配置文件
配置文件中的路径配置不再配置到Controller,model层面,只配置到外一层和公共模块下。
'application' => [
'appDir' => APP_PATH . '/',
'modelsDir' => APP_PATH . '/common/models/',
'migrationsDir' => APP_PATH . '/migrations/',
'cacheDir' => BASE_PATH . '/cache/',
'baseUri' => preg_replace('/public([\/\\\\])index.php$/', '', $_SERVER["PHP_SELF"]),
]
6.4 routes.php路由文件
配置路由
<?php
$router = $di->getRouter();
foreach ($application->getModules() as $key => $module) {
$namespace = preg_replace('/Module$/', 'Controllers', $module["className"]);
$router->add('/'.$key.'/:params', [
'namespace' => $namespace,
'module' => $key,
'controller' => 'index',
'action' => 'index',
'params' => 1
])->setName($key);
$router->add('/'.$key.'/:controller/:params', [
'namespace' => $namespace,
'module' => $key,
'controller' => 1,
'action' => 'index',
'params' => 2
]);
$router->add('/'.$key.'/:controller/:action/:params', [
'namespace' => $namespace,
'module' => $key,
'controller' => 1,
'action' => 2,
'params' => 3
]);
}
6.5 loader.php自动加载文件
<?php
use Phalcon\Loader;
$loader = new Loader();
/**
* 注册命名空间;common下的公共模型,库等
*/
$loader->registerNamespaces([
'PhalconModules\Models' => APP_PATH . '/common/models/',
'PhalconModules' => APP_PATH . '/common/library/',
]);
/**
* 注册模块下的类
*/
$loader->registerClasses([
'PhalconModules\Modules\Frontend\Module' => APP_PATH . '/modules/frontend/Module.php', // web模块的Module.php
'PhalconModules\Modules\Cli\Module' => APP_PATH . '/modules/cli/Module.php' // cli模块的Module.php
]);
$loader->register();
6.6 Module.php模块文件
在单模块项目中的路径加载配置,服务加载配置等需要区别开不同模块的,都移到了Module.php文件,以实现 ModuleDefinitionInterface 模块定义接口的形式实现。
比如Web模块的Module.php文件:
<?php
namespace PhalconModules\Modules\Frontend;
use Phalcon\DiInterface;
use Phalcon\Loader;
use Phalcon\Mvc\View;
use Phalcon\Mvc\View\Engine\Php as PhpEngine;
use Phalcon\Mvc\ModuleDefinitionInterface;
class Module implements ModuleDefinitionInterface
{
/**
* 注册模块的自动加载
*
* @param DiInterface $di
*/
public function registerAutoloaders(DiInterface $di = null)
{
$loader = new Loader();
/* 在这里定义当前模块下的Controllers,Models等目录路径 */
$loader->registerNamespaces([
'PhalconModules\Modules\Frontend\Controllers' => __DIR__ . '/controllers/',
'PhalconModules\Modules\Frontend\Models' => __DIR__ . '/models/',
]);
$loader->register();
}
/**
* 注册模块的服务
*
* @param DiInterface $di
*/
public function registerServices(DiInterface $di)
{
/**
* 设置视图组件
*/
$di->set('view', function () {
$view = new View();
$view->setDI($this);
$view->setViewsDir(__DIR__ . '/views/'); // 视图路径
$view->registerEngines([
'.volt' => 'voltShared',
'.phtml' => PhpEngine::class
]); // 注册视图引擎
return $view;
});
}
}
cli模块的Module.php:
<?php
namespace PhalconModules\Modules\Cli;
use Phalcon\DiInterface;
use Phalcon\Loader;
use Phalcon\Mvc\ModuleDefinitionInterface;
class Module implements ModuleDefinitionInterface
{
/**
* 注册模块的自动加载
*
* @param DiInterface $di
*/
public function registerAutoloaders(DiInterface $di = null)
{
$loader = new Loader();
$loader->registerNamespaces([
'PhalconModules\Modules\Cli\Tasks' => __DIR__ . '/tasks/',
]);
$loader->register();
}
/**
* 注册模块的服务
*
* @param DiInterface $di
*/
public function registerServices(DiInterface $di)
{
}
}
根据这样的一个套路,我们可以引入更多的模块。
6.7 小结
多模块的设计思路,按照这个套路,我们可以自由地搭建起我们的框架并应用到项目中。
0 条评论
来做第一个留言的人吧!