php-mvc-project/php-mvc
Composer 安装命令:
composer require php-mvc-project/php-mvc
包简介
Implementation of the MVC (Model-View-Controller) architectural pattern in PHP.
关键字:
README 文档
README
The best implementation of the Model-View-Controller architectural pattern in PHP!
Features
- Templates
- Routing
- Filters
- Cache
- Validation
- Data annotation
- Security
Requirements
- PHP 7.x
Installation
$ composer require php-mvc-project/php-mvc
Server Configuration
The server must send the entire request to the ./index.php file.
Apache
<IfModule mod_rewrite.c> RewriteEngine On # redirect /index.php to / RewriteCond %{THE_REQUEST} ^[A-Z]{3,9}\ /.*index\.php RewriteRule ^index.php/?(.*)$ $1 [R=301,L] # process all requests through index.php, except for actually existing files RewriteCond %{REQUEST_FILENAME} !-d RewriteCond %{REQUEST_FILENAME} !-f RewriteRule ^(.*)$ index.php/$1?%{QUERY_STRING} [QSA,L] </IfModule>
nginx
location / { try_files $uri $uri/ /index.php?$args; }
Basic Usage
Create the following structure in the project root directory:
. ├── controllers # controllers folder ├─── HomeController.php # class of the home controller ├─── *Controller.php # classes of others controllers ├── models # models folder ├── views # views folder ├─── home # views folder of the home controller ├─── ... # folders for other controllers ├─── shared # shared views └── index.php # index file
./index.php
<?php // use aoutoload (recommended) require_once getcwd() . '/vendor/autoload.php'; // or include the required files // require_once getcwd() . '/vendor/php-mvc-project/php-mvc/src/index.php'; // import the AppBuilder class use PhpMvc\AppBuilder; // be sure to specify the namespace of your application AppBuilder::useNamespace('RootNamespaceOfYourApp'); // use session if you need AppBuilder::useSession(); // routes AppBuilder::routes(function($routes) { // skip all the paths that point to the folder /content $routes->ignore('content/{*file}'); // default route $routes->add('default', '{controller=Home}/{action=index}/{id?}'); }); // build AppBuilder::build();
IMPORTANT: You must use namespaces in your application. Be sure to specify the root namespace of your application using the
AppBuilder::useNamespace(string)function.
./controllers/HomeController.php
<?php // make sure you add the Controllers child name to the root namespace of your application namespace RootNamespaceOfYourApp\Controllers; // import the base class of the controller use PhpMvc\Controller; // expand your class with the base controller class class HomeController extends Controller { public function index() { // use the content function to return text content: return $this->content('Hello, world!'); // create to the ./view/home/index.php // and use view function to return this view: // return $this->view(); } }
IMPORTANT: The names of all controller classes must end with the
Controllersuffix. For example:HomeController,AccountController,TestControlleretc.
Structure
Your projects must implements the strict structure:
. ├── content # static content (images, css, scripts, etc) ├─── ... # any files and folders ├── controllers # controllers folder ├─── HomeController.php # class of the home controller ├─── *Controller.php # classes of others controllers ├── filters # filters folder ├─── *.php # classes of models ├── models # models folder ├─── *.php # classes of models ├── views # views folder ├─── ... # views for specific controllers ├─── shared # shared views ├─── ... # common view files that are not associated with specific controllers ├── index.php # index file └── ... # any of your files and folders
And adhere to the following rules:
-
Folder names must be in lowercase.
-
The views filenames must be in lowercase.
-
The file names of the controllers must be specified in the camel style, with a capital letter. The names must end with the
Controllersuffix. For example:HomeController.php.
Models
The model is just classes. You can create any classes, with any structure.
It is recommended to adhere to the rule: the simpler, the better.
Using the static class Model, you can add metadata to a model in the constructor of the controller.
<?php namespace RootNamespaceOfYourApp\Controllers; use PhpMvc\Controller; use PhpMvc\Model; class AccountController extends Controller { public function __construct() { Model::set('join', 'join'); Model::required('join', 'username'); Model::required('join', 'email'); Model::required('join', 'password'); Model::required('join', 'confirmPassword'); Model::display('join', 'username', 'Username'); Model::display('join', 'email', 'Email'); Model::display('join', 'password', 'Password'); Model::display('join', 'confirmPassword', 'Confirm password'); Model::compare('join', 'confirmPassword', 'password'); Model::validation('join', 'email', function($value) { return filter_var($value, \FILTER_VALIDATE_EMAIL); }); } public function join($model) { if (!isset($model)) { $model = new ModelForExample(); } return $this->view($model); } }
Views
The views files contain markup. Markup can be complete or partial.
Using the PhpMvc\Html class, you can create markup for HTML elements or output some views within other views.
For example:
<?php use PhpMvc\Html; ?> <html> <head> <title><?=Html::getTitle('Hello, world!')?></title> </head> <body> <h1>Hello, world!</h1> <?=Html::actionLink('Go to second view', 'second'))?><br /> <?=Html::textBox('anyname')?><br /> <?=Html::checkbox('rememberMe')?><br /><br /> <!--Render ./view/shared/footer.php--> <?php Html::render('footer'); ?> </body> </html>
Use the helper class PhpMvc\View to customize the behavior of the view:
<?php use PhpMvc\View; // model intance (for the convenience of working with the IDE) $model = new RootNamespaceOfYourApp\Models\AnyClass(); // set layout View::setLayout('layout.php'); // set title View::setTitle('Test title'); // iject model (from action) View::injectModel($model); ?> <h1><?=isset($model) ? $model->anyProperty : 'empty'?></h1> <!--...-->
Controllers
The controller classes names must match the controllers filenames.
For example: file name is TestController.php, class name is TestController.
Each controller class must inherit from the PhpMvc\Controller class.
The controller classes must contain action methods.
The action names must match filenames of views.
For example: view file is index.php, action name is index.
All methods of actions must have the modifier public.
The names of the action methods must not start with the underscore (_).
class HomeController extends PhpMvc\Controller { public function index() { return $this->view(); } public function hello() { return $this->view(); } public function other() { return $this->view(); } }
Each action can take any number of parameters.
The parameters can be received from the query string, or from the POST data.
The following example shows the output of parameters retrieved from the query string:
class TestController extends PhpMvc\Controller { public function get($search = '', $page = 1, $limit = 10) { return $this->content( 'search: ' . $search . chr(10) . 'page: ' . $page . chr(10) . 'limit: ' . $limit ); } }
Request:
GET /test/get?search=hello&page=123&limit=100
Response:
search: hello
page: 123
limit: 100
Below is an example of obtaining a model sent by the POST method:
class AnyNameModelClass { public $search; public $page; public $limit; }
class TestController extends PhpMvc\Controller { public function post($anyName) { return $this->content( 'search: ' . $anyName->search . chr(10) . 'page: ' . $anyName->page . chr(10) . 'limit: ' . $anyName->limit ); } }
Request:
POST /test/post
search=hello&page=123&limit=100
Response:
search: hello
page: 123
limit: 100
Methods of action can return different results, in addition to views.
You can use the ready-made methods of the base class of the controller to output the data in the required format:
$this->view([string $viewOrModel = null[, object $model = null[, string $layout = null]]])$this->json(mixed $data[, int $options = 0[, int $depth = 512]])$this->file(string $path[, string $contentType = null[, string|bool $downloadName = null]])$this->content(string $content[, string $contentType = 'text/plain'])$this->statusCode(int $statusCode[, string $statusDescription = null])$this->notFound([string $statusDescription = null])$this->unauthorized([string $statusDescription = null])$this->redirect(string $url)$this->redirectPermanent(string $url)$this->redirectPreserveMethod(string $url)$this->redirectPermanentPreserveMethod(string $url)$this->redirectToAction(string $actionName[, string $controllerName = null[, array $routeValues = null[, string $fragment = null]]])
Instead of the presented methods, you can independently create instances of the desired results and return them:
class AnyController extends PhpMvc\Controller { public function example() { $view = new ViewResult(); // set the name of an existing view file $view->viewFile = '~/view/abc/filename.php'; // set the title $view->title = 'The view is created programmatically'; // set the layout file name $view->layout = 'layout.php'; // create model for view $model = new Example(); $model->title = 'Hello, world!'; $model->text = 'The model contains text.'; $model->number = 42; // set model to the view $view->model = $model; // return view return $view; } }
All result classes implement the ActionResult interface.
You can create your own result classes!
Filters
Filters allow you to add handlers before and after the action. And also handle errors of the action execution.
The filters must be in the ./Filters folder.
Each filter must be inherited from the PhpMvc\ActionFilter class.
Filters can be global, or work at the level of an individual controller, or action.
Filters for specific controller or action can be set in the controller's constructor:
./controllers/TestController.php
class TestController extends Controller { public function __construct() { // add filter TestFilter to all actions of the controller Filter::add('TestFilter'); // add filter ExceptionToJson to error action Filter::add('error', 'ExceptionToJson'); } public function index() { return $this->content('Hello, world!'); } public function other() { return $this->view('~/views/home/other.php'); } public function anyname() { return $this->view(); } public function error() { throw new \Exception('Any error here!'); } }
./filters/TestFilter.php
<?php namespace RootNamespaceOfYourApp\Filters; use PhpMvc\ActionFilter; use PhpMvc\ContentResult; class TestFilter extends ActionFilter { // action executed handler public function actionExecuted($actionExecutedContext) { // check exception result if (($ex = $actionExecutedContext->getException()) === null) { // no exception, replace result $actionExecutedContext->setResult(new ContentResult('test')); } else { // set exception error message to result $actionExecutedContext->setResult(new ContentResult($ex->getMessage())); } } }
./filters/ExceptionToJson.php
<?php namespace RootNamespaceOfYourApp\Filters; use PhpMvc\ActionFilter; use PhpMvc\JsonResult; class ExceptionToJson extends ActionFilter { // error handler public function exception($exceptionContext) { // set JsonResult to action result $exceptionContext->setResult( new JsonResult( array('message' => $exceptionContext->getException()->getMessage()) ) ); // exception is handled $exceptionContext->setExceptionHandled(true); } }
Routing
You can set routing rules using the AppBuilder::routes() function, which expects the function as a parameter.
When called, an instance of RouteProvider will be passed to the function.
The add(string $name, string $template[, array $defaults = null[, array $constraints = null]]) method allows you to add a routing rule.
The ignore(string $template[, $constraints = null]) method allows you to add an ignore rule.
AppBuilder::routes(function(RouteProvider $routes) { $routes->add('default', '{controller=Home}/{action=index}/{id?}'); });
The names of the routing rules must be unique.
The higher the rule in the list (the earlier the rule was added), the higher the priority in the search for a match.
In templates you can use any valid characters in the URL.
Use curly braces to denote the elements of the route.
Each element must contain a name.
A name can point to a controller, action, or any parameter expected by the action method.
For example, template is: {controller}/{action}/{yyyy}-{mm}-{dd}.
Action is:
public function index($yyyy, $mm, $dd) { return $this->content('Date: ' . $yyyy . '-' . $mm . '-' . $dd); }
Request:
GET /home/index/2018-05-26
Response:
Date: 2018-05-26
After the element name, you can specify a default value in the template. The default value is specified using the equals sign.
For example: {controller=home}/{action=index} - default controller is HomeController, default action is index().
The default values will be used if the address does not have values for the specified path elements.
Simply put, for requests /home/index, /home and / will produce the same result with this template.
If the path element is optional, then the question (?) symbol is followed by the name.
For example: {controller=home}/{action=index}/{id?} - id is optional,
{controller=home}/{action=index}/{id} - id is required.
Route providers must implement the RouteProvider interface.
The default is DefaultRouteProvider. If necessary, you can create your own route provider.
Use the AppBuilder::useRouter(RouteProvider $routeProvider) method to change the route provider.
Caching
To use caching, you must call the AppBuilder::useCache(CacheProvider $cacheProvider) method, which should be passed to the cache provider instance.
The cache provider must implement the CacheProvider interface.
You can use the ready-made FileCacheProvider, which performs caching in the file system.
AppBuilder::useCache(new FileCacheProvider());
You can access the cache via an instance of HttpContextBase.
For example, in the controller:
$cache = $this->getHttpContext()->getCache(); $cache->add('test', 'hello, world!'); var_dump($cache->get('test'));
In the view:
$cache = View::getHttpContext()->getCache(); $cache->add('test', 'hello, world!'); var_dump($cache->get('test'));
For output caching, you can use the static OutputCache class.
Caching rules can be specified for both the controller and for an each action.
Cache rules should be specified in the constructor of the controller.
<?php namespace RootNamespaceOfYourApp\Controllers; use PhpMvc\OutputCache; use PhpMvc\OutputCacheLocation; use PhpMvc\Controller; class OutputCacheController extends Controller { public function __construct() { // caching for 10 seconds of the results of all actions of this controller OutputCache::setDuration('.', 10); // caching for 30 seconds of the results of the action of thirty OutputCache::setDuration('thirty', 30); } public function index($time) { return $this->content('time => ' . $time); } public function thirty($time) { return $this->content('time => ' . $time); } }
License
The MIT License (MIT)
Copyright © 2018, @meet-aleksey
php-mvc-project/php-mvc 适用场景与选型建议
php-mvc-project/php-mvc 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 692 次下载、GitHub Stars 达 16, 最近一次更新时间为 2018 年 05 月 27 日, 在 PHP 生态内属于活跃度较高的组件。
它主要适用于以下技术方向: 「mvc」 等业务场景。在实际项目中,围绕这些方向常见需要落地的问题包括:接口对接、性能调优、并发安全、与既有框架(Laravel / ThinkPHP / Yii / Webman 等)的兼容适配,以及生产环境的日志埋点与稳定性保障。
我们在过去多个企业项目中使用过 php-mvc-project/php-mvc 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。
基于 php-mvc-project/php-mvc 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。
线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。
承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。
与 php-mvc-project/php-mvc 相关的其它包
同方向 / 同关键字的高下载量 PHP Composer 包推荐,方便对比选型:
Kinikit - PHP Application development framework MVC component
Small extensible framework in PHP
Oauth 2 client for Ubiquity framework
Light, secure and intuitive PHP Framework.
onePlace Skeleton Module
A weather module.
统计信息
- 总下载量: 692
- 月度下载量: 0
- 日度下载量: 0
- 收藏数: 17
- 点击次数: 13
- 依赖项目数: 0
- 推荐数: 0
其他信息
- 授权协议: MIT
- 更新时间: 2018-05-27