auntiewarhol / mvcish
轻量级的MVC风格的PHP Web应用框架
Requires
- php: >=8.0
- jeremykendall/php-domain-parser: ^6.0
- monolog/monolog: ^1.23
- phpmailer/phpmailer: ^6.0
- plasticbrain/php-flash-messages: ^1.0
- symfony/http-client: ^6
- symfony/polyfill-intl-idn: ^1.28
- vanilla/htmlawed: ^2.2
README
MVCish
轻量级的MVC风格的PHP Web应用框架
关于项目
这是一个非常轻量级的(因此可能功能较少)MVC框架,类似于Yii这样的东西。
它的存在是因为我继承了一个大型旧遗留系统,我需要迁移、更新和替换。我想要一个可以轻松修改的东西,将遗留页面逐步转换为新的MVC控制的页面。
因此,它有一些同时以多种不同方式运行的能力。
这一切都没有真正文档化,因为这是我为自己编写的。
它没有任何便捷的安装脚本或其他类似的东西。目前还没有。
我不鼓励任何人使用它。但嘿,这是一个自由的世界。如果你喜欢它,那就去试试吧。
我会尽量完善文档。
(返回顶部)
安装
composer require auntiewarhol/mvcish
(返回顶部)
使用
(这里是从评论中汲取的一些东西。这只是个开始)
对于典型的单点入口使用,你的应用引导脚本可能是这样的
websiteroot/MyApp/myApp.php
<?php
use \AuntieWarhol\MVCish\MVCish;
# outside scope of MVCish but we recommend using this with csrf-magic
# which can be used with beforeRender as shown below, so here's
# some setup for that, otherwise unrelated to MVCish
if (php_sapi_name() != "cli") {
// instruct PHP to use secure cookie if possible
// honestly don't remember if for MVCish of csrf or why,
// but leaving here for now at least...
$secure = ((!empty($_SERVER['HTTPS'])) && $_SERVER['HTTPS'] !== 'off') ||
(isset($_SERVER['SERVER_PORT']) && ($_SERVER['SERVER_PORT'] == 443)) ||
(isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https')
? true : false;
session_set_cookie_params(
ini_get('session.cookie_lifetime'),
ini_get('session.cookie_path'),
ini_get('session.cookie_domain'),
$secure,
true
);
// only do this after setting cookie params
//function csrf_startup() {
# configure csrf magic as needed
//}
require_once(__DIR__.'/../lib/csrf-magic-1.0.4/csrf-magic.php');
}
$MVCish = new \AuntieWarhol\MVCish\MVCish([
'environment' => ENV,
'appDirectory' => __DIR__,
// We recommend using Propel for the Model
'MODEL' => [
'NAMESPACE' => '\Models\\',
'INIT' => function($MVCish) {
require_once __DIR__.'/Propel/generated-conf/config.php';
if (!empty($serviceContainer)) {
$serviceContainer->setLogger('defaultLogger', $MVCish->log('Propel'));
$serviceContainer->setLogger('myApp', $MVCish->log());
}
// get the dummy connection config propel created,
// replace with the values from our config
$db = $MVCish->Config('DATABASE');
$connection = \Propel\Runtime\Propel::getConnectionManager('myApp');
// @phpstan-ignore-next-line
$config = $connection->getConfiguration();
// @phpstan-ignore-next-line
$connection->setConfiguration(array_replace($config,[
'dsn' => 'mysql:host='.$db['HOST'].';dbname='.$db['NAME'],
'user' => $db['USER'],
'password' => $db['PASS']
]));
},
],
'beforeRender' => function($MVCish) {
if (function_exists('csrf_conf')) {
if ($MVCish->View()->view() == 'html') {
if (($name = $GLOBALS['csrf']['input-name']) &&
($token = \BF\CSRFMagic::getTokens())
) {
// stash csrf vars where Render can find them. Render will add them
// to body_attr, where our js can pick them up to add to ajax calls.
$MVCish->options['CSRF'] = [
'data-sectkn-name' => $name,
'data-sectkn-token' => $token
];
}
}
else {
//turn off csfr writing
csrf_conf('rewrite',false);
}
}
return true;
}
]);
// provide our Auth object to MVCish
$MVCish->Auth(new \MyApp\Auth($MVCish));
// If not included by another file, run now
if(get_included_files()[0] == __FILE__) {
$MVCish->Run();
}
?>
遗留文件就地使用将与此类似。进入遗留文件,构建一个如上所示的$MVCish对象,但不是调用Run(),而是按以下描述调用runController()。遗留文件原来执行的任何逻辑都将移至作为参数传递给runController的闭包中执行。
#控制器 ********************
我们可能有一个或没有单点入口;如果有,我们就从URL中确定了您想要的控制器并运行它。否则,URL将您直接带到通常的PHP文件,该文件调用runController,并将'controller'作为一个闭包传递
$MVCish->runController(function() {
#do controller stuff
$response = //
/*
Response is super flexible. To do it proper, return a Response object:
$response = $self->Response(); // $self in a controller is $MVCish.
You can set things after:
$response->success(true);
$response->messageSuccess('It worked! Go you!');
$response->data('something',$toPass);
Or when creating:
$response = $self->Response([
'success' => true,
'messages' => ['success' => 'This also worked!'],
'data' => ['something' => $toPass,'somethingElse' => $toAlsoPass]
]);
In many cases, you'll just take the one you can get back from the form validator:
$response = $self->Validator()->Response(
// Validator is a whole other thing to document, but here's a taste...
'title' => ['required' => true, 'valid'=>['maxlength'=>100]],
'instructions' => ['valid'=>['maxlength' => $self->Model('Foo')::MAX_FOO_INSTRUCTIONS]],
'widgets' => [
'required' => $someCondition ? true : false,
'valid' => function($validator,&$value) use (&$self,&$User) {
if (!is_array($value)) $value = [$value];
if (count($value) == 1 && in_array($value[0],['none','all'])) return true;
if ($self->Model('WidgetQuery')->filterByUser($User)->filterByActive(true)
->filterByWidgetId($value)->count() == count($value)
) return true;
return false;
},
'missing' => "Please select which widgets will be used."
],
'writable' => ['defaulter' => 'boolfalse'],
'expires' => [
'default' => null,
'valid'=>['date_format_to_iso' => 'm/d/Y'],
'name'=>'Expiration Date'
],
]);
(If all that passes the validator, $response->success() is now true, $response->valid()
will give you the validated form fields, etc. Otherwisem success would be false,
$response->missing() and $respons->invalid() will give you arrays of errored fields, etc.)
---
But you can also just send an array, with at minimum a 'success' key, eg:
$response = ['success' => true];
along with any other keys appropriate for the situation.
$response = ['success' => true, 'data' => ['something' => $toPass]];
Or it could just be a bool, in which case we'll convert it
$response = true;
For that matter, we'll take any evaluates-true response you send.
for example other than the typical array, you might send an object
that can serialize itself for the json view.
$response = $myJSONobject;
Or even a text string that's the actual body of the response for the client.
$response = "Not sure the use case, but there probably is one";
*/
return $response;
});
#模型 ************************
'模型'只有非常松散的耦合;只是查找请求的类,可以配置为自动添加命名空间的一部分。示例
$user = $MVCish->Model('\Models\UserQuery'); // returns '\Models\UserQuery'
$user = $MVCish->Model('UserQuery'); // same, if 'Models\' configured as MODEL_NAMESPACE
可以通过将model_initialize函数传递到MVCish选项中,在MVCish启动时为模型执行任何需要的设置工作。参见上面的myApp.php。
路线图:即将到来的更新将允许访问多个模型
#视图 ************************
当前定义的视图
'html' => true, 'json' => true, 'stream' => true,
'csv' => true, 'csvexcel' => true, 'xml' => true,
'text' => true
如果配置的或默认的templateDirectory是MyApp/MVCish/templates,则默认在MyApp/MVCish/templates/controllers中查找主控制器模板,而片段或主模板可能位于MyApp/MVCish/templates/otherdirs中。
默认不使用主模板,只是渲染控制器模板。但子类可能希望定义一个主模板,将控制器HTML插入其中。
客户端应用程序可以使用(预先加载或可自动加载的)子类。这允许应用程序创建一个renderClass,用于准备模板数据或提供主模板。应用程序可以为每个可能不同的网站部分定义一个子类(主页与账户页面、管理页面等)。该类通过MVCish->options提供,因此如果只有一个,可以在app-config.php中提供,如果有多个,则控制器可以在Run选项中设置它所使用的类。该类可以定义如下
namespace \MyApp\Render;
class Account extends \AuntieWarhol\MVCish\View\Render {
...override/add methods as needed to render an Account template
}
然后控制器可以使用:$MVCish->Run(function($self){ ...我的控制器代码 },[ 'renderClass' => '\MyApp\Render\Account' ]);
路线图:将更新以使每个视图类型的对象都变得适当,这样客户端将更容易覆盖/扩展内置对象,或创建新的对象。
##授权 **********************************
MVCish不知道任何关于身份验证/授权的事情。但是,如果您在$MVCish->Auth()上设置一个对象(见上面的myApp.php),并且该对象有一个“授权”方法,我们将在运行控制器之前调用它,并将任何作为“授权”选项传递的内容传递给它。如果方法返回true,则表示授权。如果它返回false,我们将抛出一个未经授权的异常。您的对象还可以抛出自己的\AuntieWarhol\MVCish\Exception,如果您想控制消息(或者抛出Forbidden而不是Unauthorized等)。
MyApp/lib/Auth.php
<?php
namespace MyApp;
class Auth {
public function Authorize($authorizeRoles) {
#do whatever
#if bad
return false; #or throw exception
#if ok
return true;
}
}
}?>
路线图:我不认为Auth是MVC框架应该负责的事情,但我可以理解一个应用框架应该比MVC更大,并且应该处理Auth。将探索添加插件功能,也许会创建一个PHPAuth (https://github.com/PHPAuth/PHPAuth"php::auth) 插件。或者将其作为即将到来的安装脚本的一部分,也许吧。
(返回顶部)
路线图
我过去几周一直非常忙碌,我认为我已经修复了90%我知道的笨拙的和/或未完成的事情。如上所述,我在模型和视图上还有一些工作要做,但我认为这就是清理和前期工作的全部。
我即将将其用于第二个项目的首次尝试,我将尝试使用此过程开发安装/构建启动站点的脚本,并改进文档。
完成这些事情后,我认为这可能已经准备好供公众使用了,如果有人愿意尝试它的话。
未来计划,如我之前提到的,我认为插件架构可能是下一步,以添加我认为不是MVC框架责任,但可能是应用框架责任的东西。Auth是第一个大项目,然后可能是HTML构建工具?
(样板)请参阅开放问题以获取所有建议的功能(以及已知问题)的完整列表。
(返回顶部)
贡献
您可能不应该使用这个,更不用说贡献了。但如果你坚持,我将按照这个模板提供的说明留下,因为,为什么不呢。
--
如果您有改进这个项目的建议,请分叉存储库并创建一个拉取请求。您也可以简单地创建一个带有“增强”标签的问题。别忘了给这个项目加星!再次感谢!
- 分叉项目
- 创建您的功能分支(
git checkout -b feature/AmazingFeature
) - 提交您的更改(
git commit -m '添加一些AmazingFeature'
) - 推送到分支(
git push origin feature/AmazingFeature
) - 打开拉取请求
(返回顶部)
许可证
在MIT许可证下分发。有关更多信息,请参阅LICENSE.txt
。
(返回顶部)
联系
项目链接:https://github.com/auntiewarhol/mvcish
(返回顶部)