tahona / spark-mvc
Web MVC 框架
Requires
- php: >=7.1
- doctrine/annotations: >=v1.4.0
- doctrine/common: >=v2.5.3
- dev-master
- 5.0.5
- 5.0.4
- 5.0.3
- 5.0.2
- 5.0.1
- 5.0.0
- 4.2.7
- 4.2.6
- 4.2.5
- 4.2.4
- 4.2.3
- 4.2.2
- 4.2.1
- 4.2.0
- 4.1.9
- 4.1.8
- 4.1.7
- 4.1.6
- 4.1.5
- 4.1.4
- 4.1.3
- 4.1.2
- 4.1.1
- 4.1.0
- 4.0.0
- 3.1.17
- 3.1.16
- 3.1.15
- 3.1.14
- 3.1.13
- 3.1.12
- 3.1.11
- 3.1.10
- 3.1.9
- 3.1.8
- 3.1.7
- 3.1.6
- 3.1.5
- 3.1.4
- 3.1.3
- 3.1.2
- 3.1.1
- 3.1.0
- 3.0.14
- 3.0.13
- 3.0.12
- 3.0.11
- 3.0.10
- 3.0.9
- 3.0.8
- 3.0.7
- 3.0.6
- 3.0.5
- 3.0.4
- 3.0.3
- 3.0.2
- 3.0.1
- 3.0.0
- 2.0.21
- 2.0.20
- 2.0.19
- 2.0.18
- 2.0.17
- 2.0.16
- 2.0.15
- 2.0.14
- 2.0.13
- 2.0.12
- 2.0.11
- 2.0.10
- 2.0.9
- 2.0.8
- 2.0.7
- 2.0.6
- 2.0.5
- 2.0.4
- 2.0.3
- 2.0.2
- 2.0.1
- 2.0.0
- dev-package-change
This package is not auto-updated.
Last update: 2024-09-28 01:52:14 UTC
README
这个仓库是做什么的?
非常快的 PHP 框架,具有自动加载和注解。所有配置都由注解和 beans 处理。
工作原理
框架自动从 app/src 路径加载 beans,只需要用以下注解之一进行注解:@Component, @Service, @Configuration 或 @Repository。
可以使用 @Inject 注解实现 bean 注入。
初始化在第一个请求上完成。第二个请求将执行已经注入 beans 的 Controllers 动作。
请求执行时间非常小,因为它只是用户代码执行的时间。
因此,对于真正的生产代码,我们得到了每个请求动态数据 20ms-40ms。
快速入门
为了快速开始,下载示例项目: https://github.com/primosz67/spark-mvc-example
Index php - 解释
app/public/index.php
error_reporting(E_ALL); define("__ROOT__", __DIR__ . "/../../"); define("__VENDOR__", "../../vendor"); require __VENDOR__ . "/autoload.php";
注意:如果您将使用 doctrine 数据库框架,请在此处添加行 - "AnnotationRegistry::registerLoader('class_exists');"
框架设置
$profileName ="someNameProfile"; $engine = new Engine("przemek_config", $profileName, __ROOT__ . "app"); $engine->run();
配置
app/src/MyAppConfig.php
/** * @Configuration() * @EnableApcuBeanCache(resetParam="reset") */ class MyAppConfig { }
- resetParam - 清除缓存的参数。 (GET http://app.com?reset)。它用于开发环境,应该在生产环境中删除。
控制器
1. 带有注解控制器的标准控制器
app/src/MyAppController.php
/** * @Controller */ class MyAppController { /** * @Path("/index", method="GET") */ public function indexAction(Request $request) { return new PlainViewModel("Hello World"); } /** * @Path("/save", method="POST") */ public function saveAction(SomeForm $form, Request $req, $beanName) { return new JsonViewModel(array( "user"=>"TODO" )); } }
访问 localhost/get 或 localhost/index;
- 请求通过类型注入到方法中
- SomeForm 使用 "spark-form" 模块通过 "POST" 表单参数填写。
- $beanName - 框架可以通过名称或类型注入任何需要的 bean 或服务。
2. Rest 控制器
/** * @RestController */ class MyRestController { /** * @Path("/index") */ public function indexAction(Request $request) { return new SomeResultDto(); } }
注意:RestController 中的所有方法都将解析为 JSON。
SomeResultDto - 将解析为 json。
3. 创建控制器的旧方法
app/src/MyAppController.php
class MyAppController extends Controller { /** * @Path("/index") */ public function indexAction() { return new PlainViewModel("Hello World"); } /** * @Path("/get") */ public function getAction() { return new JsonViewModel(array( "user"=>"TODO" )); } /** * @Path("/newView") */ public function showNewViewAction() { return new ViewModel(array( "user"=>"TODO" )); } }
访问 localhost/get 或 localhost/index;
2. 定义用于自动加载的 bean。
注入
1. 定义用于自动加载的 bean。
/** * @Service() */ class UserService { //...some methods }
注意:要启用 apcu @EnableApcuBeanCache("reset") 以自动加载注入,请调用 http://website?reset
2. 定义其他 beans 并注入 bean。
/** * @Component() */ class AddUserHandler { /** * @Inject() * var UserService */ private $userService; /** * @PostConstruct() */ public function init() { $this->userService->doSomething(); } }
3. 在控制器中注入
class UserController extends Controller { /** * @Inject() * var UserService */ private $userService; /** * @Path("/newView") */ public function showNewViewAction() { return new ViewModel(array( "users"=>$this->userService->getAllUsers() )); } }
4. 在控制器动作方法中注入
/** * @Controller */ class UserController { /** * @Path("/newView") */ public function showNewViewAction(Request $request, UserService $userService ) { return new ViewModel(array( "users"=>$userService->getAllUsers() )); } }
视图
apc/view/{controller package}/{controllerName (without "Controller")}/{action}.tpl
- 对于 app/src/MyAppController@showNewViewAction,我们得到:apc/view/myapp/showNewView.tpl
- 对于 app/src/some/serious/package/controller/MyAppController@showNewViewAction,我们得到:apc/view/some/serious/package/myapp/showNewView.tpl
默认情况下会删除关键词 action 和 controller。
Smarty 内置路径插件
函数路径可以在 tpl 文件中使用(例如:index.tpl)
{path path="/user/register"}
命令将解析为: www.example.com/user/register
{path path="RegisterController@registerAction"}
命令将解析为: www.example.com/user/register
- com.example.RegisterController - 可选的控制器类名称
- registerAction - RegisterController 中的方法名称
{path path="@removeAction@id:4,type:blogpost"}
命令将解析为: www.example.com/blogpost/remove/4
- {controller} - 当前请求控制器将被使用
- removeAction - RegisterController 中的方法名称
- id:4,type:blogpost - 参数将解析为 /{type}/remove/{id} 的最终形式
Apcu Bean Cache
如果添加了 @EnableApcuBeanCache 注解,则通过请求 localhost:80?reset (GET 参数 "reset") 来重置 beans 并重新初始化它们。
邮件发送器
- @EnableMailer - TODO
- spark.mailer.enabled (true/false)- 属性
@Annotations
Spark 框架的核心。
- @Component,@Service,@Repository,@Configuration - 做的是相同的事情,但关键是目的。
- @Bean
- @PostConstruct
- @Inject
- @Bean
应用程序参数
$this->config
基本参数:app.path - 到/app目录的第一路径 src.path - 到/app/src目录的路径 app.paths - 在不同上下文中的/app目录的路径或路径集合
获取参数
$appPath = $this-config->getProperty("app.path");
更新或设置参数
$this-config->setProperty("customModule.some.property.", "/my/new/path");
自定义模块加载
如果您创建了一个用于其他项目的通用模块,请记住使用@Bean注解创建bean。这将更容易一次性添加新模块。
在您的某些应用程序配置中添加其他OtherModuleConfig。
/** * @Bean */ public function otherBeanConfiguration () { return new OtherModuleConfig(); }
OtherModuleConfig中的所有@Bean注解都将被创建并注入到您的类中。
多个数据库连接(示例)
处理多个连接。要创建Doctrine的EntityManager,您可以使用简单的EntityManagerFactory。
/** * @Inject() * @var EntityManagerFactory */ private $entityManagerFactory; /** * @Bean() */ public function entityManager() { return $this->entityManagerFactory->createEntityManager($this->getDataSource()); } /** * @Bean() */ public function superEntityManager() { return $this->entityManagerFactory->createEntityManager($this->getDataSourceSuper()); } public function getDataSource() { $dbConfig = new DataSource(); $dbConfig->setDbname("my-db"); $dbConfig->setHost("127.0.0.1"); $dbConfig->setUsername("root"); $dbConfig->setPassword("test"); $dbConfig->setPackages([ "com/myapp/user/domain" //path to doctrine entity ]); return $dbConfig; } public function getDataSourceSuper() { $dbConfig = new DataSource(); $dbConfig->setDbname("super"); $dbConfig->setHost("127.0.0.1"); $dbConfig->setUsername("root"); $dbConfig->setPassword("test"); return $dbConfig; }
注意:如果您想使用与基本EntityManager不同的实体管理器的CrudDao,请使用@OverrideInject注解。
/** * @OverrideInject(oldName="entityManager", newName="houseManager") */ class MySuperDao extends CrudDao { }
国际化
Bean定义
/** * @Bean */ public function anyPrefixNameMessageResource() { return new LangResourcePath(array( "pl"=>array( "/house/house_pl.properties" ), "cz"=> array(...), "en"=>array(...), )); }
"pl"、"cz"、"en"是带有"lang"键的cookie值;
- 属性文件/house/house_pl.properties
core.thank.you.message=Thank You {0} and {1}
- 在PHP中使用
/** * @Inject * @var langMessageResource */ private $langMessageResource; ... $this->langMessageResource->get("core.thank.you.message", array("John", "Trevor"));
- 在smarty中使用
{lang code="core.thank.you.message" value=["John", "Trevor"]}
结果:感谢John和Trevor
@Path
注解定义
@Path(path="/login/{test}", method="get")
在Controller类中获取路径参数;
$this->getParam("test");
通过组件多路径获取
示例是用于动态菜单模块,当添加新项目或类时将弹出。
/** * @Inject */ private $beanProvider; public function getMenuModules() { return $beanProvider->getByType(MenuModule::class); }
拦截器
/** * @Component */ class UserInterceptor implements HandlerInterceptor { /** * @Inject */ private $someUserHolder; public function preHandle(Request $request) { //Do something like redirect or add values to request or auto-conversion(id to entity); } public function postHandle(Request $request, ViewModel $viewModel) { $viewModel->add("loggedUser", $someUserHolder->getUserFromSession()) } }
PHP cli的命令行为
首先,创建Command实现的类
/** * @Component() */ class ExampleCommand implements Command { /** * @Inject() * @var SomeBean */ private $someBean; public function getName() :string{ return "example:exampleCommandCommand"; } public function execute(InputInterface $in, OutputInterface $out) : void { $out->writeln("executing " . $this->getName()); //Example .... $this->someBean->doSomething() $out->writeObject($this->someBean->getSomething()); $out->writeln("finish!"); } }
在控制台执行
php app/public/index.php command=example:exampleCommand profile=production
输出
executing example:exampleCommand
object(...)
finish!
内置缓存服务
对于缓存数据库请求或加载文件数据来说是一件好事。注解可以与不同的缓存一起使用。甚至可以自定义实现spark/cache/Cache接口的缓存bean。
如何使用
在Bean类中添加@Cache注解。
/** * @Cache(name="cache", key="user {0}.id", time=10) */ public function getLeaderByCompany($company){ return $this->someDao->getByCompanyId($company->getId()) }
- "name"是实现spark\cache\Cache接口的bean的名称。
- 添加应用程序所需ApcCache作为默认名称="cache"
- "time" - 可选参数,表示分钟(10分钟)
- "key"参数用于区分缓存值
配置文件
$profileName = "production"; $engine = new Engine("przemek_config",$profileName, __ROOT__ . "app");
@Configuration @Profile(name="production") class SomeProductionConfig(){ .. } @Configuration @Profile(name="development") class SomeDevelopmentConfig(){ .. }
在这种情况下,SomeDevelopmentConfig不会被添加到容器中,以及在其中声明的bean(@Bean)。
错误处理 - 示例
class NotFoundErrorHandler extends ExceptionResolver { public function doResolveException($ex) { if ($ex instanceof RouteNotFoundException || $ex instanceof EntityNotFoundException) { ResponseHelper::setCode(HttpCode::$NOT_FOUND); $viewModel = new ViewModel(); $viewModel->setViewName("/house/web/error/notFound"); return $viewModel; } return null; } public function getOrder() { return 400; } }
其中错误处理器具有等于0的顺序,将首先被调用。如果您返回ViewModel,处理将停止,视图将作为响应返回。
事件总线
/** * @Inject */ private $eventBus; ... $this->eventBus->post(new SomeEvent());
事件定义
class SomeEvent implements Event {...}
订阅
@Component class SomeListener { /** * @Subscribe */ public function handleSomeCase(SomeEvent $event) { //...logic } }
AnnotationHandler
添加您自己的注解
class NewAnnotationHandler extends AnnotationHandler{
...
}
安装 - Composer - 加速
composer dump-autoload -a
性能 - 一些数字
测试案例:具有小型数据库的实际项目。AB(Apache benchmark)请求10000和1000个并行连接。
- Smarty编译:在每次请求(开发模式)中渲染Smarty模板。
- Smarty:生产环境下的smarty。
- Smarty + @Cache(Redis):使用@Cache注解对数据库请求进行缓存(我们使用Redis进行缓存)。当有更多对数据库的调用时,这可以提高性能。
安装 - Composer
{
"autoload": {
"psr-4": {"": "app/src/"}
},
"require": {
"smarty/smarty": "3.1.27",
"tahona/spark-mvc": "*"
}
}